summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/appops/OWNERS1
-rw-r--r--core/api/current.txt2
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/ContextImpl.java8
-rw-r--r--core/java/android/app/servertransaction/WindowTokenClientController.java164
-rw-r--r--core/java/android/content/AttributionSource.java63
-rw-r--r--core/java/android/content/Intent.java8
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/content/pm/dex/DexMetadataHelper.java82
-rw-r--r--core/java/android/os/vibrator/persistence/VibrationXmlParser.java56
-rw-r--r--core/java/android/print/OWNERS4
-rw-r--r--core/java/android/print/pdf/OWNERS4
-rw-r--r--core/java/android/printservice/OWNERS4
-rw-r--r--core/java/android/printservice/recommendation/OWNERS4
-rw-r--r--core/java/android/view/animation/AnimationUtils.java9
-rw-r--r--core/java/android/window/WindowContextController.java8
-rw-r--r--core/java/android/window/WindowInfosListenerForTest.java15
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java30
-rw-r--r--core/java/android/window/WindowTokenClient.java98
-rw-r--r--core/res/res/values-watch/config_material.xml3
-rw-r--r--core/tests/coretests/src/android/content/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java105
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/window/WindowContextControllerTest.java10
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt57
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt108
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt128
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt63
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt)7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt)7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt)6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt1
-rw-r--r--libs/hwui/tests/macrobench/main.cpp86
-rw-r--r--media/java/android/media/MediaRoute2Info.java38
-rw-r--r--media/java/android/media/OWNERS3
-rw-r--r--media/jni/OWNERS3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java12
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java4
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java16
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java4
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java49
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt22
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt12
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt44
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt37
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt27
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt189
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java21
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java37
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java47
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/ktfmt_includes.txt4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt138
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerService.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt114
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt133
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt260
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt171
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt13
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java9
-rw-r--r--services/core/java/com/android/server/appop/OWNERS1
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java7
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java35
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java150
-rw-r--r--services/core/java/com/android/server/pm/ArchiveManager.java21
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java55
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java7
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java34
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java31
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java4
-rw-r--r--services/core/java/com/android/server/security/FileIntegrityService.java6
-rw-r--r--services/core/java/com/android/server/vibrator/TEST_MAPPING21
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java19
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java47
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java68
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java26
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java47
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java30
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java7
-rw-r--r--services/permission/OWNERS3
-rw-r--r--services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java50
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java316
-rw-r--r--services/tests/powerservicetests/Android.bp10
-rw-r--r--services/tests/powerservicetests/AndroidManifest.xml17
-rw-r--r--services/tests/powerservicetests/OWNERS1
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java)0
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java (renamed from services/tests/servicestests/src/com/android/server/power/NotifierTest.java)0
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java (renamed from services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java)0
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java)47
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java (renamed from services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java)0
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java (renamed from services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java)0
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java (renamed from services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java)0
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING18
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING18
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java177
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java8
-rw-r--r--services/tests/vibrator/AndroidManifest.xml3
-rw-r--r--services/tests/vibrator/AndroidTest.xml34
-rw-r--r--services/tests/vibrator/TEST_MAPPING18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java126
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java82
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java134
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java171
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java41
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt188
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt15
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt11
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml19
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml21
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml37
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml7
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java11
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java12
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java36
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java29
-rw-r--r--tests/permission/OWNERS1
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt1
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt10
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt71
276 files changed, 5006 insertions, 2123 deletions
diff --git a/cmds/appops/OWNERS b/cmds/appops/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/cmds/appops/OWNERS
+++ b/cmds/appops/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/core/api/current.txt b/core/api/current.txt
index ff011cf356c0..eaefe84beb1f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9694,6 +9694,7 @@ package android.content {
method public int describeContents();
method public void enforceCallingUid();
method @Nullable public String getAttributionTag();
+ method public int getDeviceId();
method @Nullable public android.content.AttributionSource getNext();
method @Nullable public String getPackageName();
method public int getPid();
@@ -9709,6 +9710,7 @@ package android.content {
ctor public AttributionSource.Builder(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource build();
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
+ method @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cf26e12dc761..083c445407dc 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -850,6 +850,7 @@ package android.content {
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
+ ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource);
method public void enforceCallingPid();
}
@@ -4235,6 +4236,7 @@ package android.window {
field public final boolean isTrustedOverlay;
field public final boolean isVisible;
field @NonNull public final String name;
+ field @NonNull public final android.graphics.Matrix transform;
field @NonNull public final android.os.IBinder windowToken;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4eaebe07777f..59b0dacd7cd7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -27,7 +27,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UiContext;
-import android.app.servertransaction.WindowTokenClientController;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
@@ -3277,8 +3276,7 @@ class ContextImpl extends Context {
// if this Context is not a WindowContext. WindowContext finalization is handled in
// WindowContext class.
if (mToken instanceof WindowTokenClient && mOwnsToken) {
- WindowTokenClientController.getInstance().detachIfNeeded(
- (WindowTokenClient) mToken);
+ ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
}
super.finalize();
}
@@ -3306,7 +3304,7 @@ class ContextImpl extends Context {
final WindowTokenClient token = new WindowTokenClient();
final ContextImpl context = systemContext.createWindowContextBase(token, displayId);
token.attachContext(context);
- WindowTokenClientController.getInstance().attachToDisplayContent(token, displayId);
+ token.attachToDisplayContent(displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
context.mOwnsToken = true;
@@ -3465,7 +3463,7 @@ class ContextImpl extends Context {
AttributionSource attributionSource = new AttributionSource(Process.myUid(),
Process.myPid(), mOpPackageName, attributionTag,
(renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null,
- nextAttributionSource);
+ getDeviceId(), nextAttributionSource);
// If we want to access protected data on behalf of another app we need to
// tell the OS that we opt in to participate in the attribution chain.
if (nextAttributionSource != null) {
diff --git a/core/java/android/app/servertransaction/WindowTokenClientController.java b/core/java/android/app/servertransaction/WindowTokenClientController.java
deleted file mode 100644
index 28e2040de9d5..000000000000
--- a/core/java/android/app/servertransaction/WindowTokenClientController.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static android.view.WindowManager.LayoutParams.WindowType;
-import static android.view.WindowManagerGlobal.getWindowManagerService;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.view.IWindowManager;
-import android.window.WindowContext;
-import android.window.WindowTokenClient;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Singleton controller to manage the attached {@link WindowTokenClient}s, and to dispatch
- * corresponding window configuration change from server side.
- * @hide
- */
-public class WindowTokenClientController {
-
- private static WindowTokenClientController sController;
-
- private final Object mLock = new Object();
-
- /** Mapping from a client defined token to the {@link WindowTokenClient} it represents. */
- @GuardedBy("mLock")
- private final ArrayMap<IBinder, WindowTokenClient> mWindowTokenClientMap = new ArrayMap<>();
-
- /** Gets the singleton controller. */
- public static WindowTokenClientController getInstance() {
- synchronized (WindowTokenClientController.class) {
- if (sController == null) {
- sController = new WindowTokenClientController();
- }
- return sController;
- }
- }
-
- /** Overrides the {@link #getInstance()} for test only. */
- @VisibleForTesting
- public static void overrideInstance(@NonNull WindowTokenClientController controller) {
- synchronized (WindowTokenClientController.class) {
- sController = controller;
- }
- }
-
- private WindowTokenClientController() {}
-
- /**
- * Attaches a {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
- *
- * @param client The {@link WindowTokenClient} to attach.
- * @param type The window type of the {@link WindowContext}
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @param options The window context launched option
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
- @WindowType int type, int displayId, @Nullable Bundle options) {
- final Configuration configuration;
- try {
- configuration = getWindowManagerService()
- .attachWindowContextToDisplayArea(client, type, displayId, options);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- if (configuration == null) {
- return false;
- }
- onWindowContainerTokenAttached(client, displayId, configuration);
- return true;
- }
-
- /**
- * Attaches a {@link WindowTokenClient} to a {@code DisplayContent}.
- *
- * @param client The {@link WindowTokenClient} to attach.
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayContent(@NonNull WindowTokenClient client, int displayId) {
- final IWindowManager wms = getWindowManagerService();
- // #createSystemUiContext may call this method before WindowManagerService is initialized.
- if (wms == null) {
- return false;
- }
- final Configuration configuration;
- try {
- configuration = wms.attachToDisplayContent(client, displayId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- if (configuration == null) {
- return false;
- }
- onWindowContainerTokenAttached(client, displayId, configuration);
- return true;
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
- *
- * @param client The {@link WindowTokenClient} to attach.
- * @param windowToken the window token to associated with
- */
- public void attachToWindowToken(@NonNull WindowTokenClient client,
- @NonNull IBinder windowToken) {
- try {
- getWindowManagerService().attachWindowContextToWindowToken(client, windowToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- // We don't report configuration change for now.
- synchronized (mLock) {
- mWindowTokenClientMap.put(client.asBinder(), client);
- }
- }
-
- /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
- public void detachIfNeeded(@NonNull WindowTokenClient client) {
- synchronized (mLock) {
- if (mWindowTokenClientMap.remove(client.asBinder()) == null) {
- return;
- }
- }
- try {
- getWindowManagerService().detachWindowContextFromWindowContainer(client);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private void onWindowContainerTokenAttached(@NonNull WindowTokenClient client, int displayId,
- @NonNull Configuration configuration) {
- synchronized (mLock) {
- mWindowTokenClientMap.put(client.asBinder(), client);
- }
- client.onConfigurationChanged(configuration, displayId,
- false /* shouldReportConfigChange */);
- }
-}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 3dddbc0fb90e..d0bb2b9b6eeb 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -115,14 +115,14 @@ public final class AttributionSource implements Parcelable {
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @NonNull IBinder token) {
this(uid, Process.INVALID_PID, packageName, attributionTag, token,
- /*renouncedPermissions*/ null, /*next*/ null);
+ /*renouncedPermissions*/ null, Context.DEVICE_ID_DEFAULT, /*next*/ null);
}
/** @hide */
public AttributionSource(int uid, int pid, @Nullable String packageName,
@Nullable String attributionTag, @NonNull IBinder token) {
this(uid, pid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
- /*next*/ null);
+ Context.DEVICE_ID_DEFAULT, /*next*/ null);
}
/** @hide */
@@ -132,21 +132,23 @@ public final class AttributionSource implements Parcelable {
@Nullable AttributionSource next) {
this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken,
(renouncedPermissions != null)
- ? renouncedPermissions.toArray(new String[0]) : null, /*next*/ next);
+ ? renouncedPermissions.toArray(new String[0]) : null, Context.DEVICE_ID_DEFAULT,
+ /*next*/ next);
}
/** @hide */
public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) {
this(current.getUid(), current.getPid(), current.getPackageName(),
current.getAttributionTag(), current.getToken(),
- current.mAttributionSourceState.renouncedPermissions, next);
+ current.mAttributionSourceState.renouncedPermissions, current.getDeviceId(), next);
}
/** @hide */
public AttributionSource(int uid, int pid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable String[] renouncedPermissions,
+ @Nullable String attributionTag, @Nullable String[] renouncedPermissions, int deviceId,
@Nullable AttributionSource next) {
- this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, next);
+ this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, deviceId,
+ next);
}
/** @hide */
@@ -155,6 +157,16 @@ public final class AttributionSource implements Parcelable {
@Nullable String attributionTag, @NonNull IBinder token,
@Nullable String[] renouncedPermissions,
@Nullable AttributionSource next) {
+ this(uid, pid, packageName, attributionTag, token, renouncedPermissions,
+ Context.DEVICE_ID_DEFAULT, next);
+ }
+
+ /** @hide */
+ @TestApi
+ public AttributionSource(int uid, int pid, @Nullable String packageName,
+ @Nullable String attributionTag, @NonNull IBinder token,
+ @Nullable String[] renouncedPermissions,
+ int deviceId, @Nullable AttributionSource next) {
mAttributionSourceState = new AttributionSourceState();
mAttributionSourceState.uid = uid;
mAttributionSourceState.pid = pid;
@@ -162,6 +174,7 @@ public final class AttributionSource implements Parcelable {
mAttributionSourceState.packageName = packageName;
mAttributionSourceState.attributionTag = attributionTag;
mAttributionSourceState.renouncedPermissions = renouncedPermissions;
+ mAttributionSourceState.deviceId = deviceId;
mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
{next.mAttributionSourceState} : new AttributionSourceState[0];
}
@@ -197,25 +210,31 @@ public final class AttributionSource implements Parcelable {
/** @hide */
public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, next);
+ getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), next);
}
/** @hide */
public AttributionSource withPackageName(@Nullable String packageName) {
return new AttributionSource(getUid(), getPid(), packageName, getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+ getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
}
/** @hide */
public AttributionSource withToken(@NonNull Binder token) {
return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
- token, mAttributionSourceState.renouncedPermissions, getNext());
+ token, mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
}
/** @hide */
public AttributionSource withPid(int pid) {
return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+ getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
+ }
+
+ /** @hide */
+ public AttributionSource withDeviceId(int deviceId) {
+ return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
+ getToken(), mAttributionSourceState.renouncedPermissions, deviceId, getNext());
}
/** @hide */
@@ -259,6 +278,7 @@ public final class AttributionSource implements Parcelable {
try {
return new AttributionSource.Builder(uid)
.setPid(Process.myPid())
+ .setDeviceId(Context.DEVICE_ID_DEFAULT)
.setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
.build();
} catch (Exception ignored) {
@@ -497,6 +517,13 @@ public final class AttributionSource implements Parcelable {
}
/**
+ * The device ID for which permissions are checked.
+ */
+ public int getDeviceId() {
+ return mAttributionSourceState.deviceId;
+ }
+
+ /**
* Unique token for that source.
*
* @hide
@@ -662,6 +689,19 @@ public final class AttributionSource implements Parcelable {
}
/**
+ * Set the device ID for this attribution source, permission check would happen
+ * against this device ID.
+ *
+ * @return the builder
+ */
+ public @NonNull Builder setDeviceId(int deviceId) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x12;
+ mAttributionSourceState.deviceId = deviceId;
+ return this;
+ }
+
+ /**
* The next app to receive the permission protected data.
*
* @deprecated Use {@link setNextAttributionSource} instead.
@@ -703,6 +743,9 @@ public final class AttributionSource implements Parcelable {
if ((mBuilderFieldsSet & 0x10) == 0) {
mAttributionSourceState.renouncedPermissions = null;
}
+ if ((mBuilderFieldsSet & 0x12) == 0) {
+ mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
+ }
if ((mBuilderFieldsSet & 0x20) == 0) {
mAttributionSourceState.next = null;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c10062cfb89c..fe108a513ec5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6546,6 +6546,14 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_REASON = "android.intent.extra.REASON";
/**
+ * Intent extra: Whether to show the wipe progress UI or to skip it.
+ *
+ * <p>Type: boolean
+ * @hide
+ */
+ public static final String EXTRA_SHOW_WIPE_PROGRESS = "android.intent.extra.SHOW_WIPE_PROGRESS";
+
+ /**
* {@hide}
* This extra will be send together with {@link #ACTION_FACTORY_RESET}
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2948bd9b8868..bef023e1feee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1432,7 +1432,6 @@ public abstract class PackageManager {
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
- INSTALL_DONT_EXTRACT_BASELINE_PROFILES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1647,13 +1646,6 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
- /**
- * Flag parameter for {@link PackageInstaller.SessionParams} to indicate that do not extract
- * the baseline profiles when parsing the apk
- * @hide
- */
- public static final int INSTALL_DONT_EXTRACT_BASELINE_PROFILES = 1 << 27;
-
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 3b53c25d1d8b..e75aa065d3d3 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -23,9 +23,6 @@ import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
-import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.JsonReader;
@@ -36,7 +33,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.security.VerityUtils;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -48,7 +44,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
/**
* Helper class used to compute and validate the location of dex metadata files.
@@ -67,11 +62,6 @@ public class DexMetadataHelper {
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
- private static final String PROFILE_FILE_NAME = "primary.prof";
- private static final String PROFILE_METADATA_FILE_NAME = "primary.profm";
- private static final String BASELINE_PROFILE_SOURCE_RELATIVE_PATH = "dexopt/baseline.prof";
- private static final String BASELINE_PROFILE_METADATA_RELATIVE_PATH = "dexopt/baseline.profm";
-
private DexMetadataHelper() {}
/** Return true if the given file is a dex metadata file. */
@@ -323,76 +313,4 @@ public class DexMetadataHelper {
}
}
- /**
- * Extract the baseline profiles from the assets directory in the apk. Then create a
- * ZIP archive with the (.dm) file extension (e.g. foo.dm from foo.apk) to include the
- * baseline profiles and put the DexMetadata file in the same directory with the apk.
- *
- * @param assetManager The {@link AssetManager} to use.
- * @param apkPath The path of the apk
- * @return {@code true} if the extraction is successful. Otherwise, return {@code false}.
- *
- * @see #buildDexMetadataPathForApk(String)
- */
- public static boolean extractBaselineProfilesToDexMetadataFileFromApk(AssetManager assetManager,
- String apkPath) {
- if (!ApkLiteParseUtils.isApkPath(apkPath)) {
- if (DEBUG) {
- Log.d(TAG, "It is not an apk file: " + apkPath);
- }
- return false;
- }
-
- // get the name of the DexMetadata file from the path of the apk
- final File dmFile = new File(buildDexMetadataPathForApk(apkPath));
- boolean success = false;
-
- // load profile and profile metadata from assets directory in the apk
- try (InputStream profileIs = openStreamFromAssets(assetManager,
- BASELINE_PROFILE_SOURCE_RELATIVE_PATH);
- InputStream profileMetadataIs = openStreamFromAssets(assetManager,
- BASELINE_PROFILE_METADATA_RELATIVE_PATH)) {
- // Create the zip archive file and write the baseline profiles into it.
- try (FileOutputStream fos = new FileOutputStream(dmFile)) {
- try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
- zipOs.putNextEntry(new ZipEntry(PROFILE_FILE_NAME));
- zipOs.write(profileIs.readAllBytes());
- zipOs.closeEntry();
-
- zipOs.putNextEntry(new ZipEntry(PROFILE_METADATA_FILE_NAME));
- zipOs.write(profileMetadataIs.readAllBytes());
- zipOs.closeEntry();
- success = true;
- }
- }
- } catch (IOException e) {
- if (DEBUG) {
- Log.e(TAG, "Extract baseline profiles from apk failed: " + e.getMessage());
- }
- } finally {
- if (!success) {
- if (dmFile.exists()) {
- dmFile.delete();
- }
- }
- }
- return success;
- }
-
- /**
- * Loads an {@link AutoCloseInputStream} from assets with the path.
- *
- * @param assetManager The {@link AssetManager} to use.
- * @param path The source file's relative path.
- * @return An AutoCloseInputStream in case the file was successfully read.
- * @throws IOException If anything goes wrong while opening or reading the file.
- */
- private static AutoCloseInputStream openStreamFromAssets(AssetManager assetManager, String path)
- throws IOException {
- AssetFileDescriptor descriptor = assetManager.openFd(path);
- // Based on the java doc of AssetFileDescriptor#createInputStream, it will always return
- // an AutoCloseInputStream. It should be fine we cast it from FileInputStream to
- // AutoCloseInputStream here.
- return (AutoCloseInputStream) descriptor.createInputStream();
- }
}
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index d41a428355ea..e91e04ec9cd1 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -28,7 +28,6 @@ import com.android.internal.vibrator.persistence.VibrationEffectXmlParser;
import com.android.internal.vibrator.persistence.XmlConstants;
import com.android.internal.vibrator.persistence.XmlParserException;
import com.android.internal.vibrator.persistence.XmlReader;
-import com.android.internal.vibrator.persistence.XmlSerializedVibration;
import com.android.modules.utils.TypedXmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -178,25 +177,62 @@ public final class VibrationXmlParser {
// Ensure XML starts with expected root tag.
XmlReader.readDocumentStartTag(parser, XmlConstants.TAG_VIBRATION);
- int parserFlags = 0;
- if ((flags & FLAG_ALLOW_HIDDEN_APIS) != 0) {
- parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
- }
-
// Parse root tag as a vibration effect.
- XmlSerializedVibration<VibrationEffect> serializedVibration =
- VibrationEffectXmlParser.parseTag(parser, parserFlags);
+ VibrationEffect effect = parseTag(parser, flags);
// Ensure XML ends after root tag is consumed.
XmlReader.readDocumentEndTag(parser);
- return serializedVibration.deserialize();
- } catch (XmlParserException e) {
+ return effect;
+ } catch (XmlParserException | VibrationXmlParserException e) {
Slog.w(TAG, "Error parsing vibration XML", e);
return null;
}
}
+ /**
+ * Parses XML content from given open {@link TypedXmlPullParser} into a {@link VibrationEffect}.
+ *
+ * <p>The provided parser should be pointing to a start of a valid vibration XML (i.e. to a
+ * start <vibration> tag). No other parser position, including start of document, is considered
+ * valid.
+ *
+ * <p>This method parses as long as it reads a valid vibration XML, and until an end vibration
+ * tag. After a successful parsing, the parser will point to the end vibration tag (i.e. to a
+ * </vibration> tag).
+ *
+ * @throws IOException error parsing from given {@link TypedXmlPullParser}.
+ * @throws VibrationXmlParserException if the XML tag cannot be parsed into a
+ * {@link VibrationEffect}. The given {@code parser} might be pointing to a child XML tag
+ * that caused the parser failure.
+ *
+ * @hide
+ */
+ @NonNull
+ public static VibrationEffect parseTag(@NonNull TypedXmlPullParser parser, @Flags int flags)
+ throws IOException, VibrationXmlParserException {
+ int parserFlags = 0;
+ if ((flags & VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS) != 0) {
+ parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
+ }
+ try {
+ return VibrationEffectXmlParser.parseTag(parser, parserFlags).deserialize();
+ } catch (XmlParserException e) {
+ throw new VibrationXmlParserException("Error parsing vibration effect.", e);
+ }
+ }
+
+ /**
+ * Represents an error while parsing a vibration XML input.
+ *
+ * @hide
+ */
+ public static final class VibrationXmlParserException extends Exception {
+ private VibrationXmlParserException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
private VibrationXmlParser() {
}
}
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 28a242037f6a..0809de25b45c 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 47273
-svetoslavganov@android.com
-svetoslavganov@google.com
+anothermark@google.com
+kumarashishg@google.com
diff --git a/core/java/android/print/pdf/OWNERS b/core/java/android/print/pdf/OWNERS
deleted file mode 100644
index 28a242037f6a..000000000000
--- a/core/java/android/print/pdf/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 47273
-
-svetoslavganov@android.com
-svetoslavganov@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 28a242037f6a..0809de25b45c 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 47273
-svetoslavganov@android.com
-svetoslavganov@google.com
+anothermark@google.com
+kumarashishg@google.com
diff --git a/core/java/android/printservice/recommendation/OWNERS b/core/java/android/printservice/recommendation/OWNERS
deleted file mode 100644
index 28a242037f6a..000000000000
--- a/core/java/android/printservice/recommendation/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 47273
-
-svetoslavganov@android.com
-svetoslavganov@google.com
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8cca5ed..0699bc1cb734 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -32,6 +32,7 @@ import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Choreographer;
import android.view.InflateException;
import org.xmlpull.v1.XmlPullParser;
@@ -153,7 +154,13 @@ public class AnimationUtils {
*/
public static long getExpectedPresentationTimeNanos() {
AnimationState state = sAnimationState.get();
- return state.mExpectedPresentationTimeNanos;
+ if (state.animationClockLocked) {
+ return state.mExpectedPresentationTimeNanos;
+ }
+ // When this methoed is called outside of a Choreographer callback,
+ // we obtain the value of expectedPresentTimeNanos from the Choreographer.
+ // This helps avoid returning a time that could potentially be earlier than current time.
+ return Choreographer.getInstance().getLatestExpectedPresentTimeNanos();
}
/**
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index eb270e2fb2f0..4b9a957f541d 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -21,7 +21,6 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
@@ -105,8 +104,7 @@ public class WindowContextController {
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- mAttachedToDisplayArea = WindowTokenClientController.getInstance().attachToDisplayArea(
- mToken, type, displayId, options)
+ mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options)
? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED;
if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) {
Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:"
@@ -142,13 +140,13 @@ public class WindowContextController {
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
}
- WindowTokenClientController.getInstance().attachToWindowToken(mToken, windowToken);
+ mToken.attachToWindowToken(windowToken);
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
- WindowTokenClientController.getInstance().detachIfNeeded(mToken);
+ mToken.detachFromWindowContainerIfNeeded();
mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED;
if (DEBUG_ATTACH) {
Log.d(TAG, "Detach Window Context.");
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index bdbce134801d..be88e53ad507 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.InputConfig;
@@ -78,14 +79,21 @@ public class WindowInfosListenerForTest {
*/
public final boolean isVisible;
+ /**
+ * Return the transform to get the bounds from display space into window space.
+ */
+ @NonNull
+ public final Matrix transform;
+
WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId,
- @NonNull Rect bounds, int inputConfig) {
+ @NonNull Rect bounds, int inputConfig, @NonNull Matrix transform) {
this.windowToken = windowToken;
this.name = name;
this.displayId = displayId;
this.bounds = bounds;
this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
+ this.transform = transform;
}
@Override
@@ -94,7 +102,8 @@ public class WindowInfosListenerForTest {
+ ", frame=" + bounds
+ ", isVisible=" + isVisible
+ ", isTrustedOverlay=" + isTrustedOverlay
- + ", token=" + windowToken;
+ + ", token=" + windowToken
+ + ", transform=" + transform;
}
}
@@ -155,7 +164,7 @@ public class WindowInfosListenerForTest {
var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
handle.frameBottom);
windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
- bounds, handle.inputConfig));
+ bounds, handle.inputConfig, handle.transform));
}
return windowInfos;
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 632208cdb97c..849e0b32591a 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.res.TypedArray;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -33,6 +34,7 @@ import android.view.IWindowSession;
import androidx.annotation.VisibleForTesting;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -62,6 +64,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
.getInt("persist.wm.debug.predictive_back", 1) != 0;
private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
.getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
+ private static final boolean PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE =
+ SystemProperties.getInt("persist.wm.debug.predictive_back_fallback_window_attribute", 0)
+ != 0;
@Nullable
private ImeOnBackInvokedDispatcher mImeDispatcher;
@@ -500,6 +505,31 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
applicationInfo.packageName,
requestsPredictiveBack));
}
+
+ if (PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE && !requestsPredictiveBack) {
+ // Compatibility check for legacy window style flag used by Wear OS.
+ // Note on compatibility behavior:
+ // 1. windowSwipeToDismiss should be respected for all apps not opted in.
+ // 2. windowSwipeToDismiss should be true for all apps not opted in, which
+ // enables the PB animation for them.
+ // 3. windowSwipeToDismiss=false should be respected for apps not opted in,
+ // which disables PB & onBackPressed caused by BackAnimController's
+ // setTrigger(true)
+ TypedArray windowAttr =
+ context.obtainStyledAttributes(
+ new int[] {android.R.attr.windowSwipeToDismiss});
+ boolean windowSwipeToDismiss = true;
+ if (windowAttr.getIndexCount() > 0) {
+ windowSwipeToDismiss = windowAttr.getBoolean(0, true);
+ }
+ windowAttr.recycle();
+
+ if (DEBUG) {
+ Log.i(TAG, "falling back to windowSwipeToDismiss: " + windowSwipeToDismiss);
+ }
+
+ requestsPredictiveBack = windowSwipeToDismiss;
+ }
}
return requestsPredictiveBack;
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 55b823be3cb8..a208634abe78 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -23,10 +23,10 @@ import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
-import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -36,9 +36,14 @@ import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.ref.WeakReference;
@@ -65,11 +70,15 @@ public class WindowTokenClient extends IWindowToken.Stub {
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+ private IWindowManager mWms;
+
@GuardedBy("itself")
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
+ private boolean mAttachToWindowContainer;
+
private final Handler mHandler = ActivityThread.currentActivityThread().getHandler();
/**
@@ -92,6 +101,88 @@ public class WindowTokenClient extends IWindowToken.Stub {
}
/**
+ * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
+ *
+ * @param type The window type of the {@link WindowContext}
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @param options The window context launched option
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayArea(@WindowType int type, int displayId,
+ @Nullable Bundle options) {
+ try {
+ final Configuration configuration = getWindowManagerService()
+ .attachWindowContextToDisplayArea(this, type, displayId, options);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
+ *
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayContent(int displayId) {
+ final IWindowManager wms = getWindowManagerService();
+ // #createSystemUiContext may call this method before WindowManagerService is initialized.
+ if (wms == null) {
+ return false;
+ }
+ try {
+ final Configuration configuration = wms.attachToDisplayContent(this, displayId);
+ if (configuration == null) {
+ return false;
+ }
+ onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
+ mAttachToWindowContainer = true;
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
+ *
+ * @param windowToken the window token to associated with
+ */
+ public void attachToWindowToken(IBinder windowToken) {
+ try {
+ getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
+ mAttachToWindowContainer = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
+ public void detachFromWindowContainerIfNeeded() {
+ if (!mAttachToWindowContainer) {
+ return;
+ }
+ try {
+ getWindowManagerService().detachWindowContextFromWindowContainer(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IWindowManager getWindowManagerService() {
+ if (mWms == null) {
+ mWms = WindowManagerGlobal.getWindowManagerService();
+ }
+ return mWms;
+ }
+
+ /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
@@ -116,14 +207,15 @@ public class WindowTokenClient extends IWindowToken.Stub {
* {@code shouldReportConfigChange} is {@code true}, which is usually from
* {@link IWindowToken#onConfigurationChanged(Configuration, int)}
* directly, while this method could be run on any thread if it is used to initialize
- * Context's {@code Configuration} via {@link WindowTokenClientController#attachToDisplayArea}
- * or {@link WindowTokenClientController#attachToDisplayContent}.
+ * Context's {@code Configuration} via {@link #attachToDisplayArea(int, int, Bundle)}
+ * or {@link #attachToDisplayContent(int)}.
*
* @param shouldReportConfigChange {@code true} to indicate that the {@code Configuration}
* should be dispatched to listeners.
*
*/
@AnyThread
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 03d3637b150d..529f18b78e4d 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -30,9 +30,6 @@
<!-- Always overscan by default to ensure onApplyWindowInsets will always be called. -->
<bool name="config_windowOverscanByDefault">true</bool>
- <!-- Enable windowSwipeToDismiss. -->
- <bool name="config_windowSwipeToDismiss">true</bool>
-
<!-- Style the scrollbars accoridngly. -->
<drawable name="config_scrollbarThumbVertical">@drawable/scrollbar_vertical_thumb</drawable>
<drawable name="config_scrollbarTrackVertical">@drawable/scrollbar_vertical_track</drawable>
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
new file mode 100644
index 000000000000..bbc2458f5d8b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.ContentCaptureOptionsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index c0fdf9f2d527..b31af89e345b 100644
--- a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -29,10 +29,14 @@ import static org.junit.Assert.assertThrows;
import android.os.VibrationEffect;
import android.os.vibrator.PrebakedSegment;
import android.platform.test.annotations.Presubmit;
+import android.util.Xml;
+
+import com.android.modules.utils.TypedXmlPullParser;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.io.StringReader;
@@ -65,6 +69,61 @@ public class VibrationEffectXmlSerializationTest {
}
@Test
+ public void testParseTag_succeedAndParserPointsToEndVibrationTag() throws Exception {
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+ .compose();
+ String xml = "<vibration>"
+ + "<primitive-effect name=\"click\"/>"
+ + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
+ + "</vibration>";
+ VibrationEffect effect2 = VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+ .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+ .compose();
+ String xml2 = "<vibration>"
+ + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
+ + "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
+ + "</vibration>";
+
+ TypedXmlPullParser parser = createXmlPullParser(xml);
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertEndOfDocument(parser);
+
+ // Test no-issues when an end-tag follows the vibration XML.
+ // To test this, starting with the corresponding "start-tag" is necessary.
+ parser = createXmlPullParser("<next-tag>" + xml + "</next-tag>");
+ // Move the parser once to point to the "<vibration> tag.
+ parser.next();
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertEndTag(parser, "next-tag");
+
+ parser = createXmlPullParser(xml + "<next-tag>");
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertStartTag(parser, "next-tag");
+
+ parser = createXmlPullParser(xml + xml2);
+ assertParseTagSucceeds(parser, effect);
+ parser.next();
+ assertParseTagSucceeds(parser, effect2);
+ parser.next();
+ assertEndOfDocument(parser);
+ }
+
+ @Test
+ public void testParseTag_badXml_throwsException() throws Exception {
+ assertParseTagFails(
+ "<vibration>random text<primitive-effect name=\"click\"/></vibration>");
+ assertParseTagFails("<bad-tag><primitive-effect name=\"click\"/></vibration>");
+ assertParseTagFails("<primitive-effect name=\"click\"/></vibration>");
+ assertParseTagFails("<vibration><primitive-effect name=\"click\"/>");
+ }
+
+ @Test
public void testPrimitives_allSucceed() throws IOException {
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_CLICK)
@@ -172,6 +231,41 @@ public class VibrationEffectXmlSerializationTest {
assertThat(parse(xml, /* flags= */ 0)).isEqualTo(effect);
}
+ private TypedXmlPullParser createXmlPullParser(String xml) throws Exception {
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new StringReader(xml));
+ parser.next(); // read START_DOCUMENT
+ return parser;
+ }
+
+ /**
+ * Asserts parsing vibration from an open TypedXmlPullParser succeeds, and that the parser
+ * points to the end "vibration" tag.
+ */
+ private void assertParseTagSucceeds(
+ TypedXmlPullParser parser, VibrationEffect effect) throws Exception {
+ assertThat(parseTag(parser)).isEqualTo(effect);
+
+ assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG);
+ assertThat(parser.getName()).isEqualTo("vibration");
+ }
+
+ private void assertEndTag(TypedXmlPullParser parser, String expectedTagName) throws Exception {
+ assertThat(parser.getName()).isEqualTo(expectedTagName);
+ assertThat(parser.getEventType()).isEqualTo(parser.END_TAG);
+ }
+
+ private void assertStartTag(TypedXmlPullParser parser, String expectedTagName)
+ throws Exception {
+ assertThat(parser.getName()).isEqualTo(expectedTagName);
+ assertThat(parser.getEventType()).isEqualTo(parser.START_TAG);
+ }
+
+ private void assertEndOfDocument(TypedXmlPullParser parser) throws Exception {
+ assertThat(parser.getEventType()).isEqualTo(parser.END_DOCUMENT);
+ }
+
private void assertHiddenApisParserSucceeds(String xml, VibrationEffect effect)
throws IOException {
assertThat(parse(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)).isEqualTo(effect);
@@ -183,6 +277,12 @@ public class VibrationEffectXmlSerializationTest {
() -> serialize(effect, /* flags= */ 0));
}
+ private void assertParseTagFails(String xml) {
+ assertThrows("Expected parsing to fail for " + xml,
+ VibrationXmlParser.VibrationXmlParserException.class,
+ () -> parseTag(createXmlPullParser(xml)));
+ }
+
private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
String... expectedSegments) throws IOException {
assertSerializationContainsSegments(serialize(effect, /* flags= */ 0), expectedSegments);
@@ -214,6 +314,11 @@ public class VibrationEffectXmlSerializationTest {
return VibrationXmlParser.parse(new StringReader(xml), flags);
}
+ private static VibrationEffect parseTag(TypedXmlPullParser parser)
+ throws IOException, VibrationXmlParser.VibrationXmlParserException {
+ return VibrationXmlParser.parseTag(parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+ }
+
private static String serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags)
throws IOException {
StringWriter writer = new StringWriter();
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
new file mode 100644
index 000000000000..f8beac2814db
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
new file mode 100644
index 000000000000..3cd4e17d820b
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 467d555f161f..a52d2e88145f 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,13 +24,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.app.servertransaction.WindowTokenClientController;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -58,8 +56,6 @@ import org.mockito.MockitoAnnotations;
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
- private WindowTokenClientController mWindowTokenClientController;
- @Mock
private WindowTokenClient mMockToken;
@Before
@@ -67,9 +63,7 @@ public class WindowContextControllerTest {
MockitoAnnotations.initMocks(this);
mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- WindowTokenClientController.overrideInstance(mWindowTokenClientController);
- doReturn(true).when(mWindowTokenClientController).attachToDisplayArea(
- eq(mMockToken), anyInt(), anyInt(), any());
+ doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -84,7 +78,7 @@ public class WindowContextControllerTest {
public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
mController.detachIfNeeded();
- verify(mWindowTokenClientController, never()).detachIfNeeded(any());
+ verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
}
@Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index aa0a8d9fc06a..3206dd2123d5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -257,6 +257,7 @@ applications that come with the platform
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.ACCESS_INSTANT_APPS" />
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<permission name="android.permission.DELETE_CACHE_FILES"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.DUMP"/>
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 2b1515af9d07..3bb2564807b6 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,7 +18,6 @@ package android.security.keystore2;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.PackageManager;
import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
@@ -300,12 +299,6 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
return false;
}
- private static boolean hasKeyMintV2() {
- PackageManager pm = android.app.AppGlobals.getInitialApplication().getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 200)
- && !pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 300);
- }
-
@Override
protected final void addAlgorithmSpecificParametersToBegin(
@NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) {
@@ -314,12 +307,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
));
// Only add the KM_TAG_RSA_OAEP_MGF_DIGEST tag to begin() if the MGF Digest is
- // present in the key properties or KeyMint version is 200. Keys generated prior to
- // Android 14 did not have this tag (Keystore didn't add it) and hence not present in
- // imported key as well, so specifying any MGF digest tag would cause a begin()
- // operation (on an Android 14 device) to fail (with a key that was generated on
- // Android 13 or below).
- if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics) || hasKeyMintV2()) {
+ // present in the key properties. Keys generated prior to Android 14 did not have
+ // this tag (Keystore didn't add it) so specifying any MGF digest tag would cause
+ // a begin() operation (on an Android 14 device) to fail (with a key that was generated
+ // on Android 13 or below).
+ if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics)) {
parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest
));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index af8ef174b168..7699b4bfd13a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -737,12 +737,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ final ActivityOptions activityOptions1 = options1 != null
+ ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
+ final ActivityOptions activityOptions2 = options2 != null
+ ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
if (samePackage(packageName1, packageName2, userId1, userId2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent();
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ if (shortcutInfo1 != null) {
+ activityOptions1.setApplyMultipleTaskFlagForShortcut(true);
+ }
+ if (shortcutInfo2 != null) {
+ activityOptions2.setApplyMultipleTaskFlagForShortcut(true);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
pendingIntent2 = null;
@@ -754,9 +765,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
Toast.LENGTH_SHORT).show();
}
}
- mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1,
- pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio,
- remoteTransition, instanceId);
+ mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
+ activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
+ activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+ instanceId);
}
@Override
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 e52fd00e7df7..dc78c9b139f9 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
@@ -407,7 +407,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
// Rotation change of independent non display window container.
- if (change.getParent() == null
+ if (change.getParent() == null && !change.hasFlags(FLAG_IS_DISPLAY)
&& change.getStartRotation() != change.getEndRotation()) {
startRotationAnimation(startTransaction, change, info,
ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a242c72db8b3..c22cc6fbea8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -186,9 +186,12 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
- final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+ if (remoteTransition == null) return;
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s",
remoteTransition);
+
+ final IRemoteTransition remote = remoteTransition.getRemoteTransition();
if (remote == null) return;
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
index fd56a6e49d3e..8a3c2c975faa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
@@ -42,7 +42,7 @@ import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import org.junit.Assert.assertNotNull
-internal object SplitScreenUtils {
+object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
private const val DRAG_DURATION_MS = 1_000L
private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 0f9579d58929..69c8ecd5644d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.appcompat
import android.content.Context
-import android.system.helpers.CommandsHelper
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTestData
@@ -29,15 +28,18 @@ import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowKeepVisible
import com.android.wm.shell.flicker.layerKeepVisible
-import org.junit.After
+
import org.junit.Assume
import org.junit.Before
+import org.junit.Rule
abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val letterboxApp = LetterboxAppHelper(instrumentation)
- lateinit var cmdHelper: CommandsHelper
- private lateinit var letterboxStyle: HashMap<String, String>
+
+ @JvmField
+ @Rule
+ val letterboxRule: LetterboxRule = LetterboxRule()
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -52,50 +54,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
@Before
fun before() {
- cmdHelper = CommandsHelper.getInstance(instrumentation)
- Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
- letterboxStyle = mapLetterboxStyle()
- resetLetterboxStyle()
- setLetterboxEducationEnabled(false)
- }
-
- @After
- fun after() {
- resetLetterboxStyle()
- }
-
- private fun mapLetterboxStyle(): HashMap<String, String> {
- val res = cmdHelper.executeShellCommand("wm get-letterbox-style")
- val lines = res.lines()
- val map = HashMap<String, String>()
- for (line in lines) {
- val keyValuePair = line.split(":")
- if (keyValuePair.size == 2) {
- val key = keyValuePair[0].trim()
- map[key] = keyValuePair[1].trim()
- }
- }
- return map
- }
-
- private fun getLetterboxStyle(): HashMap<String, String> {
- if (!::letterboxStyle.isInitialized) {
- letterboxStyle = mapLetterboxStyle()
- }
- return letterboxStyle
- }
-
- private fun resetLetterboxStyle() {
- cmdHelper.executeShellCommand("wm reset-letterbox-style")
- }
-
- private fun setLetterboxEducationEnabled(enabled: Boolean) {
- cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled")
- }
-
- private fun isIgnoreOrientationRequest(): Boolean {
- val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request")
- return res != null && res.contains("true")
+ Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
}
fun FlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
@@ -115,7 +74,7 @@ abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
/** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */
private fun assumeLetterboxRoundedCornersEnabled() {
- Assume.assumeTrue(getLetterboxStyle().getValue("Corner radius") != "0")
+ Assume.assumeTrue(letterboxRule.hasCornerRadius)
}
fun assertLetterboxAppVisibleAtStartAndEnd() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
new file mode 100644
index 000000000000..5a1136f97c6f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.app.Instrumentation
+import android.system.helpers.CommandsHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * JUnit Rule to handle letterboxStyles and states
+ */
+class LetterboxRule(
+ private val withLetterboxEducationEnabled: Boolean = false,
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
+) : TestRule {
+
+ private val execAdb: (String) -> String = {cmd -> cmdHelper.executeShellCommand(cmd)}
+ private lateinit var _letterboxStyle: MutableMap<String, String>
+
+ val letterboxStyle: Map<String, String>
+ get() {
+ if (!::_letterboxStyle.isInitialized) {
+ _letterboxStyle = mapLetterboxStyle()
+ }
+ return _letterboxStyle
+ }
+
+ val cornerRadius: Int?
+ get() = asInt(letterboxStyle["Corner radius"])
+
+ val hasCornerRadius: Boolean
+ get() {
+ val radius = cornerRadius
+ return radius != null && radius > 0
+ }
+
+ val isIgnoreOrientationRequest: Boolean
+ get() = execAdb("wm get-ignore-orientation-request")?.contains("true") ?: false
+
+ override fun apply(base: Statement?, description: Description?): Statement {
+ resetLetterboxStyle()
+ _letterboxStyle = mapLetterboxStyle()
+ val isLetterboxEducationEnabled = _letterboxStyle.getValue("Is education enabled")
+ var hasLetterboxEducationStateChanged = false
+ if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) {
+ hasLetterboxEducationStateChanged = true
+ execAdb("wm set-letterbox-style --isEducationEnabled " +
+ withLetterboxEducationEnabled)
+ }
+ return try {
+ object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ base!!.evaluate()
+ }
+ }
+ } finally {
+ if (hasLetterboxEducationStateChanged) {
+ execAdb("wm set-letterbox-style --isEducationEnabled " +
+ isLetterboxEducationEnabled
+ )
+ }
+ resetLetterboxStyle()
+ }
+ }
+
+ private fun mapLetterboxStyle(): HashMap<String, String> {
+ val res = execAdb("wm get-letterbox-style")
+ val lines = res.lines()
+ val map = HashMap<String, String>()
+ for (line in lines) {
+ val keyValuePair = line.split(":")
+ if (keyValuePair.size == 2) {
+ val key = keyValuePair[0].trim()
+ map[key] = keyValuePair[1].trim()
+ }
+ }
+ return map
+ }
+
+ private fun resetLetterboxStyle() {
+ execAdb("wm reset-letterbox-style")
+ }
+
+ private fun asInt(str: String?): Int? = try {
+ str?.toInt()
+ } catch (e: NumberFormatException) {
+ null
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index a7bd2584ba23..67d5718e6c1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@ import org.junit.runners.Parameterized
/**
* Test launching app in size compat mode.
*
- * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsOther:OpenAppInSizeCompatModeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
new file mode 100644
index 000000000000..e6ca261a317f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:OpenTransparentActivityTest`
+ *
+ * Actions:
+ * ```
+ * Launch a letteboxed app and then a transparent activity from it. We test the bounds
+ * are the same.
+ * ```
+ *
+ * Notes:
+ * ```
+ * Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [BaseTest]
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ letterboxTranslucentLauncherApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ waitAndGetLaunchTransparent()?.click() ?: error("Launch Transparent not found")
+ }
+ teardown {
+ letterboxTranslucentApp.exit(wmHelper)
+ letterboxTranslucentLauncherApp.exit(wmHelper)
+ }
+ }
+
+ /**
+ * Checks the transparent activity is launched on top of the opaque one
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityIsLaunchedOnTopOfOpaqueActivity() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(letterboxTranslucentLauncherApp)
+ .then()
+ .isAppWindowOnTop(letterboxTranslucentApp)
+ }
+ }
+
+ /**
+ * Checks that the activity is letterboxed
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityIsLetterboxed() {
+ flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) }
+ }
+
+ /**
+ * Checks that the translucent activity inherits bounds from the opaque one.
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityInheritsBoundsFromOpaqueActivity() {
+ flicker.assertLayersEnd {
+ this.visibleRegion(letterboxTranslucentApp)
+ .coversExactly(visibleRegion(letterboxTranslucentLauncherApp).region)
+ }
+ }
+
+ /**
+ * Checks that the translucent activity has rounded corners
+ */
+ @Postsubmit
+ @Test
+ fun translucentActivityHasRoundedCorners() {
+ flicker.assertLayersEnd {
+ this.hasRoundedCorners(letterboxTranslucentApp)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory
+ .nonRotationTests(supportedRotations = listOf(Rotation.ROTATION_90))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
index e875aae431a1..68fa8d2fc2e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -32,7 +32,9 @@ import org.junit.runners.Parameterized
/**
* Test launching a fixed portrait letterboxed app in landscape and repositioning to the right.
*
- * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` Actions:
+ * To run this test: `atest WMShellFlickerTestsOther:RepositionFixedPortraitAppTest`
+ *
+ * Actions:
*
* ```
* Launch a fixed portrait app in landscape to letterbox app
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
index a18a144b4bf1..fcb6931af9a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@ import org.junit.runners.Parameterized
/**
* Test restarting app in size compat mode.
*
- * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsOther:RestartAppInSizeCompatModeTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
new file mode 100644
index 000000000000..ea0392cee95a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.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.wm.shell.flicker.appcompat
+
+import android.content.Context
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.BaseTest
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+
+abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ protected val context: Context = instrumentation.context
+ protected val letterboxTranslucentLauncherApp = LetterboxAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.LaunchTransparentActivity.LABEL,
+ component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent()
+ )
+ protected val letterboxTranslucentApp = LetterboxAppHelper(
+ instrumentation,
+ launcherName = ActivityOptions.TransparentActivity.LABEL,
+ component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent()
+ )
+
+ @JvmField
+ @Rule
+ val letterboxRule: LetterboxRule = LetterboxRule()
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
+ }
+
+ protected fun FlickerTestData.waitAndGetLaunchTransparent(): UiObject2? =
+ device.wait(
+ Until.findObject(By.text("Launch Transparent")),
+ FIND_TIMEOUT
+ )
+
+ protected fun FlickerTestData.goBack() = device.pressBack()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index 92b62273d8cb..2494054c1fae 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
import org.junit.Test
-@RequiresDevice
-class CopyContentInSplitGesturalNavPortraitBenchmark : CopyContentInSplit(Rotation.ROTATION_0) {
+class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 566adec75615..57943ec4c8fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
import org.junit.Test
-@RequiresDevice
-class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) {
+class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index e6d56b5c94d3..6f0e202ae70f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavLandscapeBenchmark :
+class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 6752c58bd568..dac8fa24b289 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavPortraitBenchmark :
+class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 7c9ab9939dd0..baecc161d227 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark :
+class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 4b795713cb23..3063ea564b58 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
import org.junit.Test
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark :
+class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index 71ef48bea686..41660ba68385 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
import org.junit.Test
-@RequiresDevice
-class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) {
+class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 04950799732e..c41ffb786cd9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
import org.junit.Test
-@RequiresDevice
-class DragDividerToResizeGesturalNavLandscapeBenchmark : DragDividerToResize(Rotation.ROTATION_90) {
+class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index c78729c6dc92..afde55bf519f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 30bce2f657b1..3765fc42d2c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index b33ea7c89158..1e128fd3e47f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 07a86a57117b..7767872cb7fd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index 9a1d12787b9d..4ca4bd1d6d8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 266e268a3537..2d9d258ecc54 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 83fc30bceb7b..c9282acc5a2f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index b2f19299c7f0..68c6d6cccec1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index dae92dddbfec..304529ec83fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark :
+class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 732047ba38ad..53a6b449df2b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
import org.junit.Test
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark :
+class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 1de7efd7970a..a4678301be12 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark :
+class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 1a046aa5b09e..15242330a554 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
import org.junit.Test
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark :
+class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index 6e88f0eddee8..0389659457e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index d26a29c80583..7fadf184d74b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 4a552b0aed6a..148cc522e64d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index b7376eaea66d..0641fb03527c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index b2d05e4a2632..741f871e2c5f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 6de31b1315e4..778e2d6a048a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
import org.junit.Test
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index b074f2c161c9..dcdca70e0f11 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark :
- SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index aab18a6d27b9..3c69311a7028 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
import org.junit.Test
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark :
- SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 840401c23a91..c6566f5bec80 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
-@RequiresDevice
@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark : UnlockKeyguardToSplitScreen() {
+class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index c402aa4444d8..bb1a5025c5dc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -14,19 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
-@RequiresDevice
@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() {
+class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index d44d1779a3f6..9f9d4bb1a2a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -47,6 +47,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
fun setup() {
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f3f32eb6897c..e227999c2432 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -14,30 +14,32 @@
* limitations under the License.
*/
-#include "tests/common/LeakChecker.h"
-#include "tests/common/TestScene.h"
-
-#include "Properties.h"
-#include "hwui/Typeface.h"
-#include "HardwareBitmapUploader.h"
-#include "renderthread/RenderProxy.h"
-
+#include <android-base/parsebool.h>
#include <benchmark/benchmark.h>
+#include <errno.h>
+#include <fcntl.h>
#include <fnmatch.h>
#include <getopt.h>
#include <pthread.h>
#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
+
+#include <regex>
#include <string>
#include <unordered_map>
#include <vector>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include "HardwareBitmapUploader.h"
+#include "Properties.h"
+#include "hwui/Typeface.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/LeakChecker.h"
+#include "tests/common/TestScene.h"
using namespace android;
+using namespace android::base;
using namespace android::uirenderer;
using namespace android::uirenderer::test;
@@ -69,6 +71,9 @@ OPTIONS:
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
+ --benchmark_list_tests Lists the tests that would run but does not run them
+ --benchmark_filter=<regex> Filters the test set to the given regex. If prefixed with `-` and test
+ that doesn't match the given regex is run
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
--skip-leak-check Skips the memory leak check
--report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run
@@ -140,6 +145,9 @@ static bool setBenchmarkFormat(const char* format) {
if (!strcmp(format, "tabular")) {
gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
} else if (!strcmp(format, "json")) {
+ // We cannot print the leak check if outputing to JSON as that will break
+ // JSON parsers since it's not JSON-formatted
+ gRunLeakCheck = false;
gBenchmarkReporter.reset(new benchmark::JSONReporter());
} else {
fprintf(stderr, "Unknown format '%s'\n", format);
@@ -160,6 +168,24 @@ static bool setRenderer(const char* renderer) {
return true;
}
+static void addTestsThatMatchFilter(std::string spec) {
+ if (spec.empty() || spec == "all") {
+ spec = "."; // Regexp that matches all benchmarks
+ }
+ bool isNegativeFilter = false;
+ if (spec[0] == '-') {
+ spec.replace(0, 1, "");
+ isNegativeFilter = true;
+ }
+ std::regex re(spec, std::regex_constants::extended);
+ for (auto& iter : TestScene::testMap()) {
+ if ((isNegativeFilter && !std::regex_search(iter.first, re)) ||
+ (!isNegativeFilter && std::regex_search(iter.first, re))) {
+ gRunTests.push_back(iter.second);
+ }
+ }
+}
+
// 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 {
@@ -170,6 +196,8 @@ enum {
ReportFrametime,
CpuSet,
BenchmarkFormat,
+ BenchmarkListTests,
+ BenchmarkFilter,
Onscreen,
Offscreen,
Renderer,
@@ -179,14 +207,16 @@ enum {
}
static const struct option LONG_OPTIONS[] = {
- {"frames", required_argument, nullptr, 'f'},
- {"repeat", required_argument, nullptr, 'r'},
+ {"count", required_argument, nullptr, 'c'},
+ {"runs", required_argument, nullptr, 'r'},
{"help", no_argument, nullptr, 'h'},
{"list", no_argument, nullptr, LongOpts::List},
{"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu},
{"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime},
{"cpuset", required_argument, nullptr, LongOpts::CpuSet},
{"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
+ {"benchmark_list_tests", optional_argument, nullptr, LongOpts::BenchmarkListTests},
+ {"benchmark_filter", required_argument, nullptr, LongOpts::BenchmarkFilter},
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
{"renderer", required_argument, nullptr, LongOpts::Renderer},
@@ -197,8 +227,12 @@ static const struct option LONG_OPTIONS[] = {
static const char* SHORT_OPTIONS = "c:r:h";
void parseOptions(int argc, char* argv[]) {
+ benchmark::BenchmarkReporter::Context::executable_name = (argc > 0) ? argv[0] : "unknown";
+
int c;
bool error = false;
+ bool listTestsOnly = false;
+ bool testsAreFiltered = false;
opterr = 0;
while (true) {
@@ -272,6 +306,21 @@ void parseOptions(int argc, char* argv[]) {
}
break;
+ case LongOpts::BenchmarkListTests:
+ if (!optarg || ParseBool(optarg) == ParseBoolResult::kTrue) {
+ listTestsOnly = true;
+ }
+ break;
+
+ case LongOpts::BenchmarkFilter:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ addTestsThatMatchFilter(optarg);
+ testsAreFiltered = true;
+ break;
+
case LongOpts::Renderer:
if (!optarg) {
error = true;
@@ -346,11 +395,18 @@ void parseOptions(int argc, char* argv[]) {
}
}
} while (optind < argc);
- } else {
+ } else if (gRunTests.empty() && !testsAreFiltered) {
for (auto& iter : TestScene::testMap()) {
gRunTests.push_back(iter.second);
}
}
+
+ if (listTestsOnly) {
+ for (auto& iter : gRunTests) {
+ std::cout << iter.name << std::endl;
+ }
+ exit(EXIT_SUCCESS);
+ }
}
int main(int argc, char* argv[]) {
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 13936603e604..91fa873078fc 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -450,27 +450,27 @@ public final class MediaRoute2Info implements Parcelable {
public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
"android.media.route.feature.REMOTE_GROUP_PLAYBACK";
- final String mId;
- final CharSequence mName;
- final List<String> mFeatures;
+ private final String mId;
+ private final CharSequence mName;
+ private final List<String> mFeatures;
@Type
- final int mType;
- final boolean mIsSystem;
- final Uri mIconUri;
- final CharSequence mDescription;
+ private final int mType;
+ private final boolean mIsSystem;
+ private final Uri mIconUri;
+ private final CharSequence mDescription;
@ConnectionState
- final int mConnectionState;
- final String mClientPackageName;
- final String mPackageName;
- final int mVolumeHandling;
- final int mVolumeMax;
- final int mVolume;
- final String mAddress;
- final Set<String> mDeduplicationIds;
- final Bundle mExtras;
- final String mProviderId;
- final boolean mIsVisibilityRestricted;
- final Set<String> mAllowedPackages;
+ private final int mConnectionState;
+ private final String mClientPackageName;
+ private final String mPackageName;
+ private final int mVolumeHandling;
+ private final int mVolumeMax;
+ private final int mVolume;
+ private final String mAddress;
+ private final Set<String> mDeduplicationIds;
+ private final Bundle mExtras;
+ private final String mProviderId;
+ private final boolean mIsVisibilityRestricted;
+ private final Set<String> mAllowedPackages;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index 6d6a9f8eb98c..bbe5e06bb282 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -10,5 +10,8 @@ include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file *Image* = file:/graphics/java/android/graphics/OWNERS
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=set noparent
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+
# Haptics team also works on Ringtone
per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index 96894d15d8b1..e12d828733fa 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -3,3 +3,6 @@ per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,kumarashis
# extra for TV related files
per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
+
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 3505cfb9d38a..74f04e093162 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -33,8 +33,6 @@ import android.view.View;
import androidx.annotation.Nullable;
-import java.io.File;
-
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
@@ -101,14 +99,8 @@ public class InstallFailed extends AlertActivity {
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
-
- if ("package".equals(packageURI.getScheme())) {
- as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
- pm.getApplicationIcon(appInfo));
- } else {
- final File sourceFile = new File(packageURI.getPath());
- as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
- }
+ as = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+ PackageUtil.AppSnippet.class);
// Store label for dialog
mLabel = as.label;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 7bea33971259..1088acef0fb0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -16,6 +16,7 @@
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.PendingIntent;
@@ -86,7 +87,8 @@ public class InstallInstalling extends AlertActivity {
// ContentResolver.SCHEME_FILE
// STAGED_SESSION_ID extra contains an ID of a previously staged install session.
final File sourceFile = new File(mPackageURI.getPath());
- PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+ PackageUtil.AppSnippet as = getIntent()
+ .getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index ff991d2f7ee3..fbc9525d4615 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -30,7 +29,6 @@ import android.widget.Button;
import androidx.annotation.Nullable;
-import java.io.File;
import java.util.List;
/**
@@ -65,18 +63,8 @@ public class InstallSuccess extends AlertActivity {
ApplicationInfo appInfo =
intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mAppPackageName = appInfo.packageName;
- Uri packageURI = intent.getData();
-
- // Set header icon and title
- PackageManager pm = getPackageManager();
-
- if ("package".equals(packageURI.getScheme())) {
- mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
- pm.getApplicationIcon(appInfo));
- } else {
- File sourceFile = new File(packageURI.getPath());
- mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
- }
+ mAppSnippet = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+ PackageUtil.AppSnippet.class);
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index bc7fa02e497c..b66679af3fe3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -84,6 +84,7 @@ public class PackageInstallerActivity extends AlertActivity {
static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
+ static final String EXTRA_APP_SNIPPET = "EXTRA_APP_SNIPPET";
private static final String ALLOW_UNKNOWN_SOURCES_KEY =
PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
@@ -708,6 +709,9 @@ public class PackageInstallerActivity extends AlertActivity {
if (stagedSessionId > 0) {
newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
}
+ if (mAppSnippet != null) {
+ newIntent.putExtra(EXTRA_APP_SNIPPET, mAppSnippet);
+ }
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index c2d4f18d1c7c..5880a29c6c46 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -28,8 +28,13 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
@@ -117,7 +122,7 @@ public class PackageUtil {
icon);
}
- static final class AppSnippet {
+ static final class AppSnippet implements Parcelable {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
@@ -125,10 +130,52 @@ public class PackageUtil {
this.icon = icon;
}
+ private AppSnippet(Parcel in) {
+ label = in.readString();
+ Bitmap bmp = in.readParcelable(getClass().getClassLoader(), Bitmap.class);
+ icon = new BitmapDrawable(Resources.getSystem(), bmp);
+ }
+
@Override
public String toString() {
return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(label.toString());
+ Bitmap bmp = getBitmapFromDrawable(icon);
+ dest.writeParcelable(bmp, 0);
+ }
+
+ private Bitmap getBitmapFromDrawable(Drawable drawable) {
+ // Create an empty bitmap with the dimensions of our drawable
+ final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ // Associate it with a canvas. This canvas will draw the icon on the bitmap
+ final Canvas canvas = new Canvas(bmp);
+ // Draw the drawable in the canvas. The canvas will ultimately paint the drawable in the
+ // bitmap held within
+ drawable.draw(canvas);
+
+ return bmp;
+ }
+
+ public static final Parcelable.Creator<AppSnippet> CREATOR = new Parcelable.Creator<>() {
+ public AppSnippet createFromParcel(Parcel in) {
+ return new AppSnippet(in);
+ }
+
+ public AppSnippet[] newArray(int size) {
+ return new AppSnippet[size];
+ }
+ };
}
/**
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 a0ff216875a2..ddb92b19b0cf 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
@@ -27,14 +27,17 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
-import androidx.compose.material3.Divider
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+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.SettingsBody
@@ -80,11 +83,22 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
}
@Composable
- fun FooterAppVersion() {
+ fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
if (packageInfo.versionName == null) return
- Divider()
- Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+ HorizontalDivider()
+ Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(R.string.version_text, packageInfo.versionNameBidiWrapped))
+ if (showPackageName) {
+ SettingsBody(packageInfo.packageName)
+ }
+ }
+ }
+
+ @Composable
+ private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
+ val context = LocalContext.current
+ return remember {
+ DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
index e84668735fab..155905be4261 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.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.
@@ -19,26 +19,26 @@ package com.android.settingslib.spaprivileged.template.app
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import com.android.settingslib.spaprivileged.model.app.AppRecord
@Composable
fun <T : AppRecord> AppListItemModel<T>.AppListSwitchItem(
- onClick: () -> Unit,
checked: State<Boolean?>,
changeable: State<Boolean>,
onCheckedChange: ((newChecked: Boolean) -> Unit)?,
) {
- TwoTargetSwitchPreference(
+ SwitchPreference(
model = object : SwitchPreferenceModel {
override val title = label
override val summary = this@AppListSwitchItem.summary
+ override val icon = @Composable {
+ AppIcon(record.app, SettingsDimension.appIconItemSize)
+ }
override val checked = checked
override val changeable = changeable
override val onCheckedChange = onCheckedChange
},
- icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
- onClick = onClick,
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
new file mode 100644
index 000000000000..99d38401829e
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.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.settingslib.spaprivileged.template.app
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+
+@Composable
+fun <T : AppRecord> AppListItemModel<T>.AppListTwoTargetSwitchItem(
+ onClick: () -> Unit,
+ checked: State<Boolean?>,
+ changeable: State<Boolean>,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+ TwoTargetSwitchPreference(
+ model = object : SwitchPreferenceModel {
+ override val title = label
+ override val summary = this@AppListTwoTargetSwitchItem.summary
+ override val checked = checked
+ override val changeable = changeable
+ override val onCheckedChange = onCheckedChange
+ },
+ icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
+ onClick = onClick,
+ )
+}
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 bb56c10b28e3..6831e56596cc 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
@@ -93,6 +93,7 @@ class AppInfoTest {
val packageInfo = PackageInfo().apply {
applicationInfo = APP
versionName = VERSION_NAME
+ packageName = PACKAGE_NAME
}
val appInfoProvider = AppInfoProvider(packageInfo)
@@ -105,9 +106,45 @@ class AppInfoTest {
composeTestRule.onNodeWithText("version $VERSION_NAME").assertIsDisplayed()
}
+ @Test
+ fun footerAppVersion_developmentEnabled_packageNameIsDisplayed() {
+ val packageInfo = PackageInfo().apply {
+ applicationInfo = APP
+ versionName = VERSION_NAME
+ packageName = PACKAGE_NAME
+ }
+ val appInfoProvider = AppInfoProvider(packageInfo)
+
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ appInfoProvider.FooterAppVersion(true)
+ }
+ }
+ composeTestRule.onNodeWithText(PACKAGE_NAME).assertIsDisplayed()
+ }
+
+
+ @Test
+ fun footerAppVersion_developmentDisabled_packageNameDoesNotExist() {
+ val packageInfo = PackageInfo().apply {
+ applicationInfo = APP
+ versionName = VERSION_NAME
+ packageName = PACKAGE_NAME
+ }
+ val appInfoProvider = AppInfoProvider(packageInfo)
+
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ appInfoProvider.FooterAppVersion(false)
+ }
+ }
+ composeTestRule.onNodeWithText(PACKAGE_NAME).assertDoesNotExist()
+ }
+
private companion object {
const val LABEL = "Label"
const val VERSION_NAME = "VersionName"
+ const val PACKAGE_NAME = "package.name"
val APP = object : ApplicationInfo() {
override fun loadLabel(pm: PackageManager) = LABEL
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
index abdcd3bc5527..2fd1b10dade8 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.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.
@@ -43,7 +43,6 @@ class AppListSwitchItemTest {
fun appLabel_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(null),
changeable = stateOf(false),
onCheckedChange = {},
@@ -57,7 +56,6 @@ class AppListSwitchItemTest {
fun summary_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(null),
changeable = stateOf(false),
onCheckedChange = {},
@@ -68,27 +66,9 @@ class AppListSwitchItemTest {
}
@Test
- fun title_onClick() {
- var titleClicked = false
- composeTestRule.setContent {
- ITEM_MODEL.AppListSwitchItem(
- onClick = { titleClicked = true },
- checked = stateOf(false),
- changeable = stateOf(false),
- onCheckedChange = {},
- )
- }
-
- composeTestRule.onNodeWithText(LABEL).performClick()
-
- assertThat(titleClicked).isTrue()
- }
-
- @Test
fun switch_checkIsNull() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(null),
changeable = stateOf(false),
onCheckedChange = {},
@@ -102,7 +82,6 @@ class AppListSwitchItemTest {
fun switch_checked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(true),
changeable = stateOf(false),
onCheckedChange = {},
@@ -116,7 +95,6 @@ class AppListSwitchItemTest {
fun switch_notChecked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(false),
onCheckedChange = {},
@@ -130,7 +108,6 @@ class AppListSwitchItemTest {
fun switch_changeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(true),
onCheckedChange = {},
@@ -144,7 +121,6 @@ class AppListSwitchItemTest {
fun switch_notChangeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(false),
onCheckedChange = {},
@@ -159,7 +135,6 @@ class AppListSwitchItemTest {
var switchClicked = false
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- onClick = {},
checked = stateOf(false),
changeable = stateOf(true),
onCheckedChange = { switchClicked = true },
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
new file mode 100644
index 000000000000..6e7fc8e4416c
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app
+
+import android.content.pm.ApplicationInfo
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AppListTwoTargetSwitchItemTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun appLabel_displayed() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(null),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun summary_displayed() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(null),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+ }
+
+ @Test
+ fun title_onClick() {
+ var titleClicked = false
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = { titleClicked = true },
+ checked = stateOf(false),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ assertThat(titleClicked).isTrue()
+ }
+
+ @Test
+ fun switch_checkIsNull() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(null),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertDoesNotExist()
+ }
+
+ @Test
+ fun switch_checked() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(true),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOn()
+ }
+
+ @Test
+ fun switch_notChecked() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+
+ @Test
+ fun switch_changeable() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(true),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsEnabled()
+ }
+
+ @Test
+ fun switch_notChangeable() {
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(false),
+ onCheckedChange = {},
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsNotEnabled()
+ }
+
+ @Test
+ fun switch_onClick() {
+ var switchClicked = false
+ composeTestRule.setContent {
+ ITEM_MODEL.AppListTwoTargetSwitchItem(
+ onClick = {},
+ checked = stateOf(false),
+ changeable = stateOf(true),
+ onCheckedChange = { switchClicked = true },
+ )
+ }
+
+ composeTestRule.onNode(isToggleable()).performClick()
+
+ assertThat(switchClicked).isTrue()
+ }
+
+ private companion object {
+ const val PACKAGE_NAME = "package.name"
+ const val LABEL = "Label"
+ const val SUMMARY = "Summary"
+ val APP = ApplicationInfo().apply {
+ packageName = PACKAGE_NAME
+ }
+ val ITEM_MODEL = AppListItemModel(
+ record = object : AppRecord {
+ override val app = APP
+ },
+ label = LABEL,
+ summary = stateOf(SUMMARY),
+ )
+ }
+}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 612a9282fece..1a938d6ec37e 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -179,7 +179,8 @@ public abstract class Tile implements Parcelable {
* Check whether tile has order.
*/
public boolean hasOrder() {
- return mMetaData.containsKey(META_DATA_KEY_ORDER)
+ return mMetaData != null
+ && mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
}
@@ -204,7 +205,7 @@ public abstract class Tile implements Parcelable {
CharSequence title = null;
ensureMetadataNotStale(context);
final PackageManager packageManager = context.getPackageManager();
- if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+ if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) {
// If has as uri to provide dynamic title, skip loading here. UI will later load
// at tile binding time.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f522fd13c9f8..2118d2cbf4b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -356,11 +356,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
connectDevice();
}
- public HearingAidInfo getHearingAidInfo() {
- return mHearingAidInfo;
- }
-
- public void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
+ void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
mHearingAidInfo = hearingAidInfo;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 0c1b793102bf..441d3a52b97f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.util.Log;
@@ -114,10 +115,21 @@ public class CachedBluetoothDeviceManager {
/**
* Create and return a new {@link CachedBluetoothDevice}. This assumes
* that {@link #findDevice} has already been called and returned null.
- * @param device the address of the new Bluetooth device
+ * @param device the new Bluetooth device
* @return the newly created CachedBluetoothDevice object
*/
public CachedBluetoothDevice addDevice(BluetoothDevice device) {
+ return addDevice(device, /*leScanFilters=*/null);
+ }
+
+ /**
+ * Create and return a new {@link CachedBluetoothDevice}. This assumes
+ * that {@link #findDevice} has already been called and returned null.
+ * @param device the new Bluetooth device
+ * @param leScanFilters the BLE scan filters which the device matched
+ * @return the newly created CachedBluetoothDevice object
+ */
+ public CachedBluetoothDevice addDevice(BluetoothDevice device, List<ScanFilter> leScanFilters) {
CachedBluetoothDevice newDevice;
final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
synchronized (this) {
@@ -125,7 +137,7 @@ public class CachedBluetoothDeviceManager {
if (newDevice == null) {
newDevice = new CachedBluetoothDevice(mContext, profileManager, device);
mCsipDeviceManager.initCsipDeviceIfNeeded(newDevice);
- mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice, leScanFilters);
if (!mCsipDeviceManager.setMemberDeviceIfNeeded(newDevice)
&& !mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
mCachedDevices.add(newDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index e5e57824f6ef..efba953e3c6b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -18,10 +18,13 @@ package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
import android.content.ContentResolver;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.audiopolicy.AudioProductStrategy;
+import android.os.ParcelUuid;
import android.provider.Settings;
import android.util.Log;
@@ -59,7 +62,8 @@ public class HearingAidDeviceManager {
mRoutingHelper = routingHelper;
}
- void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+ void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice,
+ List<ScanFilter> leScanFilters) {
long hiSyncId = getHiSyncId(newDevice.getDevice());
if (isValidHiSyncId(hiSyncId)) {
// Once hiSyncId is valid, assign hearing aid info
@@ -68,6 +72,21 @@ public class HearingAidDeviceManager {
.setAshaDeviceMode(getDeviceMode(newDevice.getDevice()))
.setHiSyncId(hiSyncId);
newDevice.setHearingAidInfo(infoBuilder.build());
+ } else if (leScanFilters != null && !newDevice.isHearingAidDevice()) {
+ // If the device is added with hearing aid scan filter during pairing, set an empty
+ // hearing aid info to indicate it's a hearing aid device. The info will be updated
+ // when corresponding profiles connected.
+ for (ScanFilter leScanFilter: leScanFilters) {
+ final ParcelUuid serviceUuid = leScanFilter.getServiceUuid();
+ final ParcelUuid serviceDataUuid = leScanFilter.getServiceDataUuid();
+ if (BluetoothUuid.HEARING_AID.equals(serviceUuid)
+ || BluetoothUuid.HAS.equals(serviceUuid)
+ || BluetoothUuid.HEARING_AID.equals(serviceDataUuid)
+ || BluetoothUuid.HAS.equals(serviceDataUuid)) {
+ newDevice.setHearingAidInfo(new HearingAidInfo.Builder().build());
+ break;
+ }
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 1d433e767e5b..5bc271954b25 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -59,7 +59,7 @@ public abstract class AbstractWifiMacAddressPreferenceController
@Override
public boolean isAvailable() {
- return true;
+ return mWifiManager != null;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 7a48838ced91..bff51e32c1f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -496,16 +496,6 @@ public abstract class InfoMediaManager extends MediaManager {
return info.getName();
}
- boolean shouldDisableMediaOutput(String packageName) {
- if (TextUtils.isEmpty(packageName)) {
- Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
- return true;
- }
-
- // Disable when there is no transferable route
- return getTransferableRoutes(packageName).isEmpty();
- }
-
@TargetApi(Build.VERSION_CODES.R)
boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
return sessionInfo.isSystemSession() // System sessions are not remote
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 8479df1c7666..fd5ca847877f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -453,13 +453,6 @@ public class LocalMediaManager implements BluetoothCallback {
}
/**
- * Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
- */
- public boolean shouldDisableMediaOutput(String packageName) {
- return mInfoMediaManager.shouldDisableMediaOutput(packageName);
- }
-
- /**
* Returns {@code true} if needed to enable volume seekbar, otherwise returns {@code false}.
*/
public boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 0d5de88cc394..ea10944be0e9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -33,6 +33,8 @@ import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
@@ -147,7 +149,7 @@ public class HearingAidDeviceManagerTest {
HearingAidProfile.DeviceSide.SIDE_RIGHT);
assertThat(mCachedDevice1.getHiSyncId()).isNotEqualTo(HISYNCID1);
- mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
@@ -164,12 +166,43 @@ public class HearingAidDeviceManagerTest {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
BluetoothHearingAid.HI_SYNC_ID_INVALID);
- mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
}
/**
+ * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId and hearing aid scan filter, set an
+ * empty hearing aid info on the device.
+ */
+ @Test
+ public void initHearingAidDeviceIfNeeded_hearingAidScanFilter_setHearingAidInfo() {
+ when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+ BluetoothHearingAid.HI_SYNC_ID_INVALID);
+ final ScanFilter scanFilter = new ScanFilter.Builder()
+ .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}).build();
+
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
+
+ assertThat(mCachedDevice1.isHearingAidDevice()).isTrue();
+ }
+
+ /**
+ * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId and random scan filter, not to set
+ * hearing aid info on the device.
+ */
+ @Test
+ public void initHearingAidDeviceIfNeeded_randomScanFilter_setHearingAidInfo() {
+ when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+ BluetoothHearingAid.HI_SYNC_ID_INVALID);
+ final ScanFilter scanFilter = new ScanFilter.Builder().build();
+
+ mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
+
+ assertThat(mCachedDevice1.isHearingAidDevice()).isFalse();
+ }
+
+ /**
* Test setSubDeviceIfNeeded, a device with same HiSyncId will be set as sub device
*/
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 45b5de4cf367..866ef9d6076e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -1083,51 +1083,4 @@ public class InfoMediaManagerTest {
assertThat(device.getState()).isEqualTo(STATE_SELECTED);
assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(device);
}
-
- @Test
- public void shouldDisableMediaOutput_infosIsEmpty_returnsTrue() {
- mShadowRouter2Manager.setTransferableRoutes(new ArrayList<>());
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
- }
-
- @Test
- public void shouldDisableMediaOutput_infosSizeEqual1_returnsFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final List<MediaRoute2Info> infos = new ArrayList<>();
- infos.add(info);
- mShadowRouter2Manager.setTransferableRoutes(infos);
-
- when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
- }
-
- @Test
- public void shouldDisableMediaOutput_infosSizeEqual1AndNotCastDevice_returnsFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final List<MediaRoute2Info> infos = new ArrayList<>();
- infos.add(info);
- mShadowRouter2Manager.setTransferableRoutes(infos);
-
- when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
- }
-
-
- @Test
- public void shouldDisableMediaOutput_infosSizeOverThan1_returnsFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
- final List<MediaRoute2Info> infos = new ArrayList<>();
- infos.add(info);
- infos.add(info2);
- mShadowRouter2Manager.setTransferableRoutes(infos);
-
- when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
- when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-
- assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
- }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7d8b06663f78..368115b99040 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,6 +320,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.OBSERVE_APP_USAGE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 18753fd9c0c7..006fc09fb400 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -422,9 +422,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -696,7 +693,6 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
index 57b3acd6557a..66c54f2a668e 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
@@ -21,6 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyguard_lock_padding"
+ android:importantForAccessibility="no"
android:ellipsize="marquee"
android:focusable="true"
android:gravity="center"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
index 50e5466d0325..1cb8e43cf2c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
@@ -12,12 +12,9 @@ class KeyguardClockFrame(
) : FrameLayout(context, attrs) {
private var drawAlpha: Int = 255
- init {
- setLayerType(View.LAYER_TYPE_SOFTWARE, null)
- }
-
protected override fun onSetAlpha(alpha: Int): Boolean {
- drawAlpha = alpha
+ // Ignore alpha passed from View, prefer to compute it from set values
+ drawAlpha = (255 * this.alpha * transitionAlpha).toInt()
return true
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 2377057f1fc5..d9b7bde66c67 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -69,7 +69,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
(long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED),
0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator(
- mContext, android.R.interpolator.fast_out_linear_in));
+ mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
@@ -82,8 +82,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- mLastDevicePosture = posture;
- updateMargins();
+ if (mLastDevicePosture != posture) {
+ mLastDevicePosture = posture;
+ updateMargins();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 38c07dc98471..2bdf46e1309d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -104,8 +104,10 @@ public class KeyguardPatternView extends KeyguardInputView
}
void onDevicePostureChanged(@DevicePostureInt int posture) {
- mLastDevicePosture = posture;
- updateMargins();
+ if (mLastDevicePosture != posture) {
+ mLastDevicePosture = posture;
+ updateMargins();
+ }
}
private void updateMargins() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5e84a57b488f..35198deab55f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -164,13 +164,13 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.DetectionStatus;
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.WeatherData;
@@ -1471,27 +1471,31 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private FaceAuthenticationListener mFaceAuthenticationListener =
new FaceAuthenticationListener() {
@Override
- public void onAuthenticationStatusChanged(@NonNull AuthenticationStatus status) {
- if (status instanceof AcquiredAuthenticationStatus) {
+ public void onAuthenticationStatusChanged(
+ @NonNull FaceAuthenticationStatus status
+ ) {
+ if (status instanceof AcquiredFaceAuthenticationStatus) {
handleFaceAcquired(
- ((AcquiredAuthenticationStatus) status).getAcquiredInfo());
- } else if (status instanceof ErrorAuthenticationStatus) {
- ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status;
+ ((AcquiredFaceAuthenticationStatus) status).getAcquiredInfo());
+ } else if (status instanceof ErrorFaceAuthenticationStatus) {
+ ErrorFaceAuthenticationStatus error =
+ (ErrorFaceAuthenticationStatus) status;
handleFaceError(error.getMsgId(), error.getMsg());
- } else if (status instanceof FailedAuthenticationStatus) {
+ } else if (status instanceof FailedFaceAuthenticationStatus) {
handleFaceAuthFailed();
- } else if (status instanceof HelpAuthenticationStatus) {
- HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status;
+ } else if (status instanceof HelpFaceAuthenticationStatus) {
+ HelpFaceAuthenticationStatus helpMsg =
+ (HelpFaceAuthenticationStatus) status;
handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg());
- } else if (status instanceof SuccessAuthenticationStatus) {
+ } else if (status instanceof SuccessFaceAuthenticationStatus) {
FaceManager.AuthenticationResult result =
- ((SuccessAuthenticationStatus) status).getSuccessResult();
+ ((SuccessFaceAuthenticationStatus) status).getSuccessResult();
handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
}
}
@Override
- public void onDetectionStatusChanged(@NonNull DetectionStatus status) {
+ public void onDetectionStatusChanged(@NonNull FaceDetectionStatus status) {
handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric());
}
};
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 2abdb849cd9a..e3e9b3a3754a 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
@@ -16,10 +16,10 @@
package com.android.systemui.bouncer.domain.interactor
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
@@ -35,8 +35,8 @@ constructor(
private val keyguardStateController: KeyguardStateController,
private val bouncerRepository: KeyguardBouncerRepository,
private val biometricSettingsRepository: BiometricSettingsRepository,
- private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val systemClock: SystemClock,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
var receivedDownTouch = false
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
@@ -78,7 +78,7 @@ constructor(
biometricSettingsRepository.isFingerprintEnrolled.value &&
biometricSettingsRepository.isStrongBiometricAllowed.value &&
biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
- !deviceEntryFingerprintAuthRepository.isLockedOut.value &&
+ !keyguardUpdateMonitor.isFingerprintLockedOut &&
!keyguardStateController.isUnlocked &&
!statusBarStateController.isDozing
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
index 0e224060a36f..f3a07fc53027 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
@@ -16,15 +16,52 @@
package com.android.systemui.dreams
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.dagger.DreamLog
-import javax.inject.Inject
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
/** Logs dream-related stuff to a {@link LogBuffer}. */
-class DreamLogger @Inject constructor(@DreamLog private val buffer: LogBuffer) {
- /** Logs a debug message to the buffer. */
- fun d(tag: String, message: String) {
- buffer.log(tag, LogLevel.DEBUG, { str1 = message }, { message })
- }
+class DreamLogger(buffer: MessageBuffer, tag: String) : Logger(buffer, tag) {
+ fun logDreamOverlayEnabled(enabled: Boolean) =
+ d({ "Dream overlay enabled: $bool1" }) { bool1 = enabled }
+
+ fun logIgnoreAddComplication(reason: String, complication: String) =
+ d({ "Ignore adding complication, reason: $str1, complication: $str2" }) {
+ str1 = reason
+ str2 = complication
+ }
+
+ fun logIgnoreRemoveComplication(reason: String, complication: String) =
+ d({ "Ignore removing complication, reason: $str1, complication: $str2" }) {
+ str1 = reason
+ str2 = complication
+ }
+
+ fun logAddComplication(complication: String) =
+ d({ "Add dream complication: $str1" }) { str1 = complication }
+
+ fun logRemoveComplication(complication: String) =
+ d({ "Remove dream complication: $str1" }) { str1 = complication }
+
+ fun logOverlayActive(active: Boolean) = d({ "Dream overlay active: $bool1" }) { bool1 = active }
+
+ fun logLowLightActive(active: Boolean) =
+ d({ "Low light mode active: $bool1" }) { bool1 = active }
+
+ fun logHasAssistantAttention(hasAttention: Boolean) =
+ d({ "Dream overlay has Assistant attention: $bool1" }) { bool1 = hasAttention }
+
+ fun logStatusBarVisible(visible: Boolean) =
+ d({ "Dream overlay status bar visible: $bool1" }) { bool1 = visible }
+
+ fun logAvailableComplicationTypes(types: Int) =
+ d({ "Available complication types: $int1" }) { int1 = types }
+
+ fun logShouldShowComplications(showComplications: Boolean) =
+ d({ "Dream overlay should show complications: $bool1" }) { bool1 = showComplications }
+
+ fun logShowOrHideStatusBarItem(show: Boolean, type: String) =
+ d({ "${if (bool1) "Showing" else "Hiding"} dream status bar item: $int1" }) {
+ bool1 = show
+ str1 = type
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 484bf3d51f36..01fb5227749f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -36,6 +36,9 @@ import com.android.systemui.complication.ComplicationLayoutParams.Position
import com.android.systemui.dreams.dagger.DreamOverlayModule
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.DreamLog
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -65,12 +68,14 @@ constructor(
private val mDreamInTranslationYDistance: Int,
@Named(DreamOverlayModule.DREAM_IN_TRANSLATION_Y_DURATION)
private val mDreamInTranslationYDurationMs: Long,
- private val mLogger: DreamLogger,
+ @DreamLog logBuffer: LogBuffer,
) {
companion object {
private const val TAG = "DreamOverlayAnimationsController"
}
+ private val logger = Logger(logBuffer, TAG)
+
private var mAnimator: Animator? = null
private lateinit var view: View
@@ -179,11 +184,11 @@ constructor(
doOnEnd {
mAnimator = null
mOverlayStateController.setEntryAnimationsFinished(true)
- mLogger.d(TAG, "Dream overlay entry animations finished.")
+ logger.d("Dream overlay entry animations finished.")
}
- doOnCancel { mLogger.d(TAG, "Dream overlay entry animations canceled.") }
+ doOnCancel { logger.d("Dream overlay entry animations canceled.") }
start()
- mLogger.d(TAG, "Dream overlay entry animations started.")
+ logger.d("Dream overlay entry animations started.")
}
}
@@ -242,11 +247,11 @@ constructor(
doOnEnd {
mAnimator = null
mOverlayStateController.setExitAnimationsRunning(false)
- mLogger.d(TAG, "Dream overlay exit animations finished.")
+ logger.d("Dream overlay exit animations finished.")
}
- doOnCancel { mLogger.d(TAG, "Dream overlay exit animations canceled.") }
+ doOnCancel { logger.d("Dream overlay exit animations canceled.") }
start()
- mLogger.d(TAG, "Dream overlay exit animations started.")
+ logger.d("Dream overlay exit animations started.")
}
mOverlayStateController.setExitAnimationsRunning(true)
return mAnimator as AnimatorSet
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index c2421dcbc6ca..c9748f954670 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -28,6 +28,8 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
@@ -115,10 +117,10 @@ public class DreamOverlayStateController implements
public DreamOverlayStateController(@Main Executor executor,
@Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled,
FeatureFlags featureFlags,
- DreamLogger dreamLogger) {
+ @DreamLog LogBuffer logBuffer) {
mExecutor = executor;
mOverlayEnabled = overlayEnabled;
- mLogger = dreamLogger;
+ mLogger = new DreamLogger(logBuffer, TAG);
mFeatureFlags = featureFlags;
if (mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)) {
mSupportedTypes = Complication.COMPLICATION_TYPE_NONE
@@ -126,7 +128,7 @@ public class DreamOverlayStateController implements
} else {
mSupportedTypes = Complication.COMPLICATION_TYPE_NONE;
}
- mLogger.d(TAG, "Dream overlay enabled: " + mOverlayEnabled);
+ mLogger.logDreamOverlayEnabled(mOverlayEnabled);
}
/**
@@ -134,14 +136,13 @@ public class DreamOverlayStateController implements
*/
public void addComplication(Complication complication) {
if (!mOverlayEnabled) {
- mLogger.d(TAG,
- "Ignoring adding complication due to overlay disabled: " + complication);
+ mLogger.logIgnoreAddComplication("overlay disabled", complication.toString());
return;
}
mExecutor.execute(() -> {
if (mComplications.add(complication)) {
- mLogger.d(TAG, "Added dream complication: " + complication);
+ mLogger.logAddComplication(complication.toString());
mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
}
});
@@ -152,14 +153,13 @@ public class DreamOverlayStateController implements
*/
public void removeComplication(Complication complication) {
if (!mOverlayEnabled) {
- mLogger.d(TAG,
- "Ignoring removing complication due to overlay disabled: " + complication);
+ mLogger.logIgnoreRemoveComplication("overlay disabled", complication.toString());
return;
}
mExecutor.execute(() -> {
if (mComplications.remove(complication)) {
- mLogger.d(TAG, "Removed dream complication: " + complication);
+ mLogger.logRemoveComplication(complication.toString());
mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
}
});
@@ -305,7 +305,7 @@ public class DreamOverlayStateController implements
* @param active {@code true} if overlay is active, {@code false} otherwise.
*/
public void setOverlayActive(boolean active) {
- mLogger.d(TAG, "Dream overlay active: " + active);
+ mLogger.logOverlayActive(active);
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE);
}
@@ -314,7 +314,7 @@ public class DreamOverlayStateController implements
* @param active {@code true} if low light mode is active, {@code false} otherwise.
*/
public void setLowLightActive(boolean active) {
- mLogger.d(TAG, "Low light mode active: " + active);
+ mLogger.logLowLightActive(active);
if (isLowLightActive() && !active) {
// Notify that we're exiting low light only on the transition from active to not active.
@@ -346,7 +346,7 @@ public class DreamOverlayStateController implements
* @param hasAttention {@code true} if has the user's attention, {@code false} otherwise.
*/
public void setHasAssistantAttention(boolean hasAttention) {
- mLogger.d(TAG, "Dream overlay has Assistant attention: " + hasAttention);
+ mLogger.logHasAssistantAttention(hasAttention);
modifyState(hasAttention ? OP_SET_STATE : OP_CLEAR_STATE, STATE_HAS_ASSISTANT_ATTENTION);
}
@@ -355,7 +355,7 @@ public class DreamOverlayStateController implements
* @param visible {@code true} if the status bar is visible, {@code false} otherwise.
*/
public void setDreamOverlayStatusBarVisible(boolean visible) {
- mLogger.d(TAG, "Dream overlay status bar visible: " + visible);
+ mLogger.logStatusBarVisible(visible);
modifyState(
visible ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_STATUS_BAR_VISIBLE);
}
@@ -373,7 +373,7 @@ public class DreamOverlayStateController implements
*/
public void setAvailableComplicationTypes(@Complication.ComplicationType int types) {
mExecutor.execute(() -> {
- mLogger.d(TAG, "Available complication types: " + types);
+ mLogger.logAvailableComplicationTypes(types);
mAvailableComplicationTypes = types;
mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
});
@@ -391,7 +391,7 @@ public class DreamOverlayStateController implements
*/
public void setShouldShowComplications(boolean shouldShowComplications) {
mExecutor.execute(() -> {
- mLogger.d(TAG, "Should show complications: " + shouldShowComplications);
+ mLogger.logShouldShowComplications(shouldShowComplications);
mShouldShowComplications = shouldShowComplications;
mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
});
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 3a284083e844..a6401b6594ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -36,6 +36,8 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
@@ -161,7 +163,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
DreamOverlayStateController dreamOverlayStateController,
UserTracker userTracker,
- DreamLogger dreamLogger) {
+ @DreamLog LogBuffer logBuffer) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -177,7 +179,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mZenModeController = zenModeController;
mDreamOverlayStateController = dreamOverlayStateController;
mUserTracker = userTracker;
- mLogger = dreamLogger;
+ mLogger = new DreamLogger(logBuffer, TAG);
// Register to receive show/hide updates for the system status bar. Our custom status bar
// needs to hide when the system status bar is showing to ovoid overlapping status bars.
@@ -346,8 +348,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
@Nullable String contentDescription) {
mMainExecutor.execute(() -> {
if (mIsAttached) {
- mLogger.d(TAG, (show ? "Showing" : "Hiding") + " dream status bar item: "
- + DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
+ mLogger.logShowOrHideStatusBarItem(
+ show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
mView.showIcon(iconType, show, contentDescription);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0670ec380861..79a1728470dc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -254,7 +254,7 @@ object Flags {
/** Migrate the indication area to the new keyguard root view. */
// TODO(b/280067944): Tracking bug.
@JvmField
- val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+ val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area")
/**
* Migrate the bottom area to the new keyguard root view.
@@ -294,6 +294,11 @@ object Flags {
@JvmField
val MIGRATE_NSSL = unreleasedFlag(242, "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(243, "migrate_keyguard_status_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
new file mode 100644
index 000000000000..eaecda52a5a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
@@ -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.systemui.flags
+
+import android.util.Log
+import com.android.systemui.Dependency
+
+/**
+ * This class promotes best practices for flag guarding System UI view refactors.
+ * * [isEnabled] allows changing an implementation.
+ * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and
+ * ensure that it is not being invoked accidentally in the post-flag refactor.
+ * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on
+ * flag-disabled builds, but with a check that should crash eng builds or tests when the
+ * expectation is violated.
+ *
+ * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it,
+ * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes
+ * inside views where injecting flag values after initialization can be error-prone.
+ */
+class ViewRefactorFlag
+private constructor(
+ private val injectedFlags: FeatureFlags?,
+ private val flag: BooleanFlag,
+ private val readFlagValue: (FeatureFlags) -> Boolean
+) {
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: UnreleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: ReleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */
+ val isEnabled by lazy {
+ @Suppress("DEPRECATION")
+ val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
+ readFlagValue(featureFlags)
+ }
+
+ /**
+ * 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.
+ *
+ * Example usage:
+ * ```
+ * public void setController(NotificationShelfController notificationShelfController) {
+ * mShelfRefactor.assertDisabled();
+ * mController = notificationShelfController;
+ * }
+ * ````
+ */
+ fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
+
+ /**
+ * 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.
+ *
+ * Example usage:
+ * ```
+ * public void setShelfIcons(NotificationIconContainer icons) {
+ * if (mShelfRefactor.expectEnabled()) {
+ * mShelfIcons = icons;
+ * }
+ * }
+ * ```
+ */
+ fun expectEnabled(): Boolean {
+ if (!isEnabled) {
+ val message = "Code path not supported when $flag is disabled."
+ Log.wtf(TAG, message, Exception(message))
+ }
+ return isEnabled
+ }
+
+ private companion object {
+ private const val TAG = "ViewRefactorFlag"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9a32e94180c7..e0834bb894b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -163,9 +163,11 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import dagger.Lazy;
@@ -290,6 +292,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public static final String SYS_BOOT_REASON_PROP = "sys.boot.reason.last";
public static final String REBOOT_MAINLINE_UPDATE = "reboot,mainline_update";
private final DreamOverlayStateController mDreamOverlayStateController;
+ private final JavaAdapter mJavaAdapter;
+ private final WallpaperRepository mWallpaperRepository;
/** The stream type that the lock sounds are tied to. */
private int mUiSoundsStreamType;
@@ -1323,6 +1327,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
KeyguardTransitions keyguardTransitions,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
+ JavaAdapter javaAdapter,
+ WallpaperRepository wallpaperRepository,
Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -1384,6 +1390,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mScreenOffAnimationController = screenOffAnimationController;
mInteractionJankMonitor = interactionJankMonitor;
mDreamOverlayStateController = dreamOverlayStateController;
+ mJavaAdapter = javaAdapter;
+ mWallpaperRepository = wallpaperRepository;
mActivityLaunchAnimator = activityLaunchAnimator;
mScrimControllerLazy = scrimControllerLazy;
@@ -1487,6 +1495,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
com.android.internal.R.anim.lock_screen_behind_enter);
mWorkLockController = new WorkLockActivityController(mContext, mUserTracker);
+
+ mJavaAdapter.alwaysCollectFlow(
+ mWallpaperRepository.getWallpaperSupportsAmbientMode(),
+ this::setWallpaperSupportsAmbientMode);
}
// TODO(b/273443374) remove, temporary util to get a feature flag
@@ -2970,6 +2982,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
private void onKeyguardExitFinished() {
+ if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
@@ -3191,13 +3204,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
flags |= StatusBarManager.DISABLE_RECENT;
}
- if (mPowerGestureIntercepted) {
+ if (mPowerGestureIntercepted && mOccluded && isSecure()) {
flags |= StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
+ " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " mPowerGestureIntercepted=" + mPowerGestureIntercepted
+ " --> flags=0x" + Integer.toHexString(flags));
}
@@ -3425,6 +3439,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
pw.print(" mPendingLock: "); pw.println(mPendingLock);
pw.print(" wakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
pw.print(" mPendingPinLock: "); pw.println(mPendingPinLock);
+ pw.print(" mPowerGestureIntercepted: "); pw.println(mPowerGestureIntercepted);
}
/**
@@ -3464,7 +3479,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
* In case it does support it, we have to fade in the incoming app, otherwise we'll reveal it
* with the light reveal scrim.
*/
- public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
+ private void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
mWallpaperSupportsAmbientMode = supportsAmbientMode;
}
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 1487408f1a0c..a5ac7c7500e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -67,9 +67,11 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import dagger.Lazy;
@@ -131,6 +133,8 @@ public class KeyguardModule {
KeyguardTransitions keyguardTransitions,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
+ JavaAdapter javaAdapter,
+ WallpaperRepository wallpaperRepository,
Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -172,6 +176,8 @@ public class KeyguardModule {
keyguardTransitions,
interactionJankMonitor,
dreamOverlayStateController,
+ javaAdapter,
+ wallpaperRepository,
shadeController,
notificationShadeWindowController,
activityLaunchAnimator,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 0b6c7c415599..ff3e77c46f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -325,6 +325,9 @@ constructor(
private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
LockPatternUtils.StrongAuthTracker(context) {
+ private val selectedUserId =
+ userRepository.selectedUserInfo.map { it.id }.distinctUntilChanged()
+
// Backing field for onStrongAuthRequiredChanged
private val _authFlags =
MutableStateFlow(AuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId)))
@@ -336,15 +339,12 @@ private class StrongAuthTracker(private val userRepository: UserRepository, cont
)
val currentUserAuthFlags: Flow<AuthenticationFlags> =
- userRepository.selectedUserInfo
- .map { it.id }
- .distinctUntilChanged()
- .flatMapLatest { userId ->
- _authFlags
- .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
- .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
- .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
- }
+ selectedUserId.flatMapLatest { userId ->
+ _authFlags
+ .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
+ .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
+ .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
+ }
/** isStrongBiometricAllowed for the current user. */
val isStrongBiometricAllowed: Flow<Boolean> =
@@ -352,16 +352,17 @@ private class StrongAuthTracker(private val userRepository: UserRepository, cont
/** isNonStrongBiometricAllowed for the current user. */
val isNonStrongBiometricAllowed: Flow<Boolean> =
- userRepository.selectedUserInfo
- .map { it.id }
- .distinctUntilChanged()
+ selectedUserId
.flatMapLatest { userId ->
_nonStrongBiometricAllowed
.filter { it.first == userId }
.map { it.second }
- .onEach { Log.d(TAG, "isNonStrongBiometricAllowed changed for current user") }
+ .onEach {
+ Log.d(TAG, "isNonStrongBiometricAllowed changed for current user: $it")
+ }
.onStart { emit(isNonStrongBiometricAllowedAfterIdleTimeout(userId)) }
}
+ .and(isStrongBiometricAllowed)
private val currentUserId
get() = userRepository.getSelectedUserInfo().id
@@ -387,3 +388,6 @@ private fun DevicePolicyManager.isFingerprintDisabled(userId: Int): Boolean =
private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
(getKeyguardDisabledFeatures(null, userId) and policy) == 0
+
+private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
+ this.combine(anotherFlow) { a, b -> a && b }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index a3d1abedcc2c..6edf40f47521 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -38,13 +38,13 @@ 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.model.AcquiredAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
@@ -88,10 +88,10 @@ interface DeviceEntryFaceAuthRepository {
val canRunFaceAuth: StateFlow<Boolean>
/** Provide the current status of face authentication. */
- val authenticationStatus: Flow<AuthenticationStatus>
+ val authenticationStatus: Flow<FaceAuthenticationStatus>
/** Provide the current status of face detection. */
- val detectionStatus: Flow<DetectionStatus>
+ val detectionStatus: Flow<FaceDetectionStatus>
/** Current state of whether face authentication is locked out or not. */
val isLockedOut: StateFlow<Boolean>
@@ -151,13 +151,13 @@ constructor(
private var cancelNotReceivedHandlerJob: Job? = null
private var halErrorRetryJob: Job? = null
- private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+ private val _authenticationStatus: MutableStateFlow<FaceAuthenticationStatus?> =
MutableStateFlow(null)
- override val authenticationStatus: Flow<AuthenticationStatus>
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = _authenticationStatus.filterNotNull()
- private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
- override val detectionStatus: Flow<DetectionStatus>
+ private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull()
private val _isLockedOut = MutableStateFlow(false)
@@ -396,18 +396,18 @@ constructor(
private val faceAuthCallback =
object : FaceManager.AuthenticationCallback() {
override fun onAuthenticationFailed() {
- _authenticationStatus.value = FailedAuthenticationStatus
+ _authenticationStatus.value = FailedFaceAuthenticationStatus
_isAuthenticated.value = false
faceAuthLogger.authenticationFailed()
onFaceAuthRequestCompleted()
}
override fun onAuthenticationAcquired(acquireInfo: Int) {
- _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+ _authenticationStatus.value = AcquiredFaceAuthenticationStatus(acquireInfo)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
- val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+ val errorStatus = ErrorFaceAuthenticationStatus(errorCode, errString.toString())
if (errorStatus.isLockoutError()) {
_isLockedOut.value = true
}
@@ -433,11 +433,11 @@ constructor(
if (faceAcquiredInfoIgnoreList.contains(code)) {
return
}
- _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+ _authenticationStatus.value = HelpFaceAuthenticationStatus(code, helpStr.toString())
}
override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
- _authenticationStatus.value = SuccessAuthenticationStatus(result)
+ _authenticationStatus.value = SuccessFaceAuthenticationStatus(result)
_isAuthenticated.value = true
faceAuthLogger.faceAuthSuccess(result)
onFaceAuthRequestCompleted()
@@ -482,7 +482,7 @@ constructor(
private val detectionCallback =
FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
faceAuthLogger.faceDetected()
- _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+ _detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
}
private var cancellationInProgress = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 52234b32b83a..9bec30052476 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -21,27 +21,29 @@ import android.hardware.biometrics.BiometricAuthenticator.Modality
import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dumpable
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
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.dump.DumpManager
-import java.io.PrintWriter
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */
interface DeviceEntryFingerprintAuthRepository {
/** Whether the device entry fingerprint auth is locked out. */
- val isLockedOut: StateFlow<Boolean>
+ val isLockedOut: Flow<Boolean>
/**
* Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
@@ -53,6 +55,9 @@ interface DeviceEntryFingerprintAuthRepository {
* Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
*/
val availableFpSensorType: Flow<BiometricType?>
+
+ /** Provide the current status of fingerprint authentication. */
+ val authenticationStatus: Flow<FingerprintAuthenticationStatus>
}
/**
@@ -69,16 +74,7 @@ constructor(
val authController: AuthController,
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Application scope: CoroutineScope,
- dumpManager: DumpManager,
-) : DeviceEntryFingerprintAuthRepository, Dumpable {
-
- init {
- dumpManager.registerDumpable(this)
- }
-
- override fun dump(pw: PrintWriter, args: Array<String?>) {
- pw.println("isLockedOut=${isLockedOut.value}")
- }
+) : DeviceEntryFingerprintAuthRepository {
override val availableFpSensorType: Flow<BiometricType?>
get() {
@@ -114,7 +110,7 @@ constructor(
else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
}
- override val isLockedOut: StateFlow<Boolean> =
+ override val isLockedOut: Flow<Boolean> =
conflatedCallbackFlow {
val sendLockoutUpdate =
fun() {
@@ -138,7 +134,7 @@ constructor(
sendLockoutUpdate()
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
- .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
override val isRunning: Flow<Boolean>
get() = conflatedCallbackFlow {
@@ -166,6 +162,93 @@ constructor(
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
+ override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+ get() = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType,
+ isStrongBiometric: Boolean,
+ ) {
+
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ SuccessFingerprintAuthenticationStatus(
+ userId,
+ isStrongBiometric,
+ ),
+ )
+ }
+
+ override fun onBiometricError(
+ msgId: Int,
+ errString: String?,
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ ErrorFingerprintAuthenticationStatus(
+ msgId,
+ errString,
+ ),
+ )
+ }
+
+ override fun onBiometricHelp(
+ msgId: Int,
+ helpString: String?,
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ HelpFingerprintAuthenticationStatus(
+ msgId,
+ helpString,
+ ),
+ )
+ }
+
+ override fun onBiometricAuthFailed(
+ biometricSourceType: BiometricSourceType,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ FailFingerprintAuthenticationStatus,
+ )
+ }
+
+ override fun onBiometricAcquired(
+ biometricSourceType: BiometricSourceType,
+ acquireInfo: Int,
+ ) {
+ sendUpdateIfFingerprint(
+ biometricSourceType,
+ AcquiredFingerprintAuthenticationStatus(
+ acquireInfo,
+ ),
+ )
+ }
+
+ private fun sendUpdateIfFingerprint(
+ biometricSourceType: BiometricSourceType,
+ authenticationStatus: FingerprintAuthenticationStatus
+ ) {
+ if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
+ return
+ }
+
+ trySendWithFailureLogging(
+ authenticationStatus,
+ TAG,
+ "new fingerprint authentication status"
+ )
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+
companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 42bee4a3bdcd..7475c4251211 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -31,11 +31,13 @@ import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -49,9 +51,11 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
@@ -148,6 +152,9 @@ interface KeyguardRepository {
/** Observable for device wake/sleep state */
val wakefulness: StateFlow<WakefulnessModel>
+ /** Observable for device screen state */
+ val screenModel: StateFlow<ScreenModel>
+
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
@@ -163,6 +170,9 @@ interface KeyguardRepository {
/** Whether quick settings or quick-quick settings is visible. */
val isQuickSettingsVisible: Flow<Boolean>
+ /** Receive an event for doze time tick */
+ val dozeTimeTick: Flow<Unit>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -204,6 +214,8 @@ interface KeyguardRepository {
fun setIsDozing(isDozing: Boolean)
fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean)
+
+ fun dozeTimeTick()
}
/** Encapsulates application state for the keyguard. */
@@ -213,6 +225,7 @@ class KeyguardRepositoryImpl
constructor(
statusBarStateController: StatusBarStateController,
wakefulnessLifecycle: WakefulnessLifecycle,
+ screenLifecycle: ScreenLifecycle,
biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardBypassController: KeyguardBypassController,
@@ -370,6 +383,13 @@ constructor(
_isDozing.value = isDozing
}
+ private val _dozeTimeTick = MutableSharedFlow<Unit>()
+ override val dozeTimeTick = _dozeTimeTick.asSharedFlow()
+
+ override fun dozeTimeTick() {
+ _dozeTimeTick.tryEmit(Unit)
+ }
+
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
@@ -559,6 +579,42 @@ constructor(
initialValue = WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
)
+ override val screenModel: StateFlow<ScreenModel> =
+ conflatedCallbackFlow {
+ val observer =
+ object : ScreenLifecycle.Observer {
+ override fun onScreenTurningOn() {
+ dispatchNewState()
+ }
+ override fun onScreenTurnedOn() {
+ dispatchNewState()
+ }
+ override fun onScreenTurningOff() {
+ dispatchNewState()
+ }
+ override fun onScreenTurnedOff() {
+ dispatchNewState()
+ }
+
+ private fun dispatchNewState() {
+ trySendWithFailureLogging(
+ ScreenModel.fromScreenLifecycle(screenLifecycle),
+ TAG,
+ "updated screen state",
+ )
+ }
+ }
+
+ screenLifecycle.addObserver(observer)
+ awaitClose { screenLifecycle.removeObserver(observer) }
+ }
+ .stateIn(
+ scope,
+ // Use Eagerly so that we're always listening and never miss an event.
+ SharingStarted.Eagerly,
+ initialValue = ScreenModel.fromScreenLifecycle(screenLifecycle),
+ )
+
override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
fun sendFpLocation() {
trySendWithFailureLogging(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index abe59b76816f..27e3a749a6c0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -18,8 +18,8 @@ package com.android.systemui.keyguard.data.repository
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,10 +40,10 @@ class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA
private val _canRunFaceAuth = MutableStateFlow(false)
override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth
- override val authenticationStatus: Flow<AuthenticationStatus>
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = emptyFlow()
- override val detectionStatus: Flow<DetectionStatus>
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = emptyFlow()
private val _isLockedOut = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt
new file mode 100644
index 000000000000..c849b8495a26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.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.keyguard.domain.interactor
+
+import android.content.res.Resources
+import android.hardware.biometrics.BiometricSourceType
+import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+/**
+ * BiometricMessage business logic. Filters biometric error/acquired/fail/success events for
+ * authentication events that should never surface a message to the user at the current device
+ * state.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class BiometricMessageInteractor
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ private val fingerprintPropertyRepository: FingerprintPropertyRepository,
+ private val indicationHelper: IndicationHelper,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ val fingerprintErrorMessage: Flow<BiometricMessage> =
+ fingerprintAuthRepository.authenticationStatus
+ .filter {
+ it is ErrorFingerprintAuthenticationStatus &&
+ !indicationHelper.shouldSuppressErrorMsg(FINGERPRINT, it.msgId)
+ }
+ .map {
+ val errorStatus = it as ErrorFingerprintAuthenticationStatus
+ BiometricMessage(
+ FINGERPRINT,
+ BiometricMessageType.ERROR,
+ errorStatus.msgId,
+ errorStatus.msg,
+ )
+ }
+
+ val fingerprintHelpMessage: Flow<BiometricMessage> =
+ fingerprintAuthRepository.authenticationStatus
+ .filter { it is HelpFingerprintAuthenticationStatus }
+ .filterNot { isPrimaryAuthRequired() }
+ .map {
+ val helpStatus = it as HelpFingerprintAuthenticationStatus
+ BiometricMessage(
+ FINGERPRINT,
+ BiometricMessageType.HELP,
+ helpStatus.msgId,
+ helpStatus.msg,
+ )
+ }
+
+ val fingerprintFailMessage: Flow<BiometricMessage> =
+ isUdfps().flatMapLatest { isUdfps ->
+ fingerprintAuthRepository.authenticationStatus
+ .filter { it is FailFingerprintAuthenticationStatus }
+ .filterNot { isPrimaryAuthRequired() }
+ .map {
+ BiometricMessage(
+ FINGERPRINT,
+ BiometricMessageType.FAIL,
+ BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ if (isUdfps) {
+ resources.getString(
+ com.android.internal.R.string.fingerprint_udfps_error_not_match
+ )
+ } else {
+ resources.getString(
+ com.android.internal.R.string.fingerprint_error_not_match
+ )
+ },
+ )
+ }
+ }
+
+ private fun isUdfps() =
+ fingerprintPropertyRepository.sensorType.map {
+ it == FingerprintSensorType.UDFPS_OPTICAL ||
+ it == FingerprintSensorType.UDFPS_ULTRASONIC
+ }
+
+ private fun isPrimaryAuthRequired(): Boolean {
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed since strong biometrics can still be
+ // used.
+ return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
+ }
+}
+
+data class BiometricMessage(
+ val source: BiometricSourceType,
+ val type: BiometricMessageType,
+ val id: Int,
+ val message: String?,
+)
+
+enum class BiometricMessageType {
+ HELP,
+ ERROR,
+ FAIL,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index 2efcd0c1ffe7..0c898befe6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -35,4 +35,8 @@ constructor(
fun setLastTapToWakePosition(position: Point) {
keyguardRepository.setLastDozeTapToWakePosition(position)
}
+
+ fun dozeTimeTick() {
+ keyguardRepository.dozeTimeTick()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index 74ef7a50fd44..141b13055889 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -16,8 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow
/**
@@ -27,10 +27,10 @@ import kotlinx.coroutines.flow.Flow
interface KeyguardFaceAuthInteractor {
/** Current authentication status */
- val authenticationStatus: Flow<AuthenticationStatus>
+ val authenticationStatus: Flow<FaceAuthenticationStatus>
/** Current detection status */
- val detectionStatus: Flow<DetectionStatus>
+ val detectionStatus: Flow<FaceDetectionStatus>
/** Can face auth be run right now */
fun canFaceAuthRun(): Boolean
@@ -72,8 +72,8 @@ interface KeyguardFaceAuthInteractor {
*/
interface FaceAuthenticationListener {
/** Receive face authentication status updates */
- fun onAuthenticationStatusChanged(status: AuthenticationStatus)
+ fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
/** Receive status updates whenever face detection runs */
- fun onDetectionStatusChanged(status: DetectionStatus)
+ fun onDetectionStatusChanged(status: FaceDetectionStatus)
}
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 7fae7522d981..1553525915d5 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
@@ -70,6 +70,8 @@ constructor(
val dozeAmount: Flow<Float> = repository.linearDozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+ /** Receive an event for doze time tick */
+ val dozeTimeTick: Flow<Unit> = repository.dozeTimeTick
/** Whether Always-on Display mode is available. */
val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
/** Doze transition information. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index 5005b6c7f0df..10dd900e437e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
@@ -31,9 +31,9 @@ import kotlinx.coroutines.flow.emptyFlow
*/
@SysUISingleton
class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
- override val authenticationStatus: Flow<AuthenticationStatus>
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
get() = emptyFlow()
- override val detectionStatus: Flow<DetectionStatus>
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = emptyFlow()
override fun canFaceAuthRun(): Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index d467225a9d63..6e7a20b092f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -30,8 +30,8 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.util.kotlin.pairwise
@@ -165,10 +165,10 @@ constructor(
repository.cancel()
}
- private val _authenticationStatusOverride = MutableStateFlow<AuthenticationStatus?>(null)
+ private val faceAuthenticationStatusOverride = MutableStateFlow<FaceAuthenticationStatus?>(null)
/** Provide the status of face authentication */
override val authenticationStatus =
- merge(_authenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
+ merge(faceAuthenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
/** Provide the status of face detection */
override val detectionStatus = repository.detectionStatus
@@ -176,13 +176,13 @@ constructor(
private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
if (repository.isLockedOut.value) {
- _authenticationStatusOverride.value =
- ErrorAuthenticationStatus(
+ faceAuthenticationStatusOverride.value =
+ ErrorFaceAuthenticationStatus(
BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
context.resources.getString(R.string.keyguard_face_unlock_unavailable)
)
} else {
- _authenticationStatusOverride.value = null
+ faceAuthenticationStatusOverride.value = null
applicationScope.launch {
faceAuthenticationLogger.authRequested(uiEvent)
repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index b354cfd27687..0f6d82ed4b5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -23,33 +23,35 @@ import android.os.SystemClock.elapsedRealtime
* Authentication status provided by
* [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
*/
-sealed class AuthenticationStatus
+sealed class FaceAuthenticationStatus
/** Success authentication status. */
-data class SuccessAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
- AuthenticationStatus()
+data class SuccessFaceAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
+ FaceAuthenticationStatus()
/** Face authentication help message. */
-data class HelpAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus()
+data class HelpFaceAuthenticationStatus(val msgId: Int, val msg: String?) :
+ FaceAuthenticationStatus()
/** Face acquired message. */
-data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationStatus()
+data class AcquiredFaceAuthenticationStatus(val acquiredInfo: Int) : FaceAuthenticationStatus()
/** Face authentication failed message. */
-object FailedAuthenticationStatus : AuthenticationStatus()
+object FailedFaceAuthenticationStatus : FaceAuthenticationStatus()
/** Face authentication error message */
-data class ErrorAuthenticationStatus(
+data class ErrorFaceAuthenticationStatus(
val msgId: Int,
val msg: String? = null,
// present to break equality check if the same error occurs repeatedly.
val createdAt: Long = elapsedRealtime()
-) : AuthenticationStatus() {
+) : FaceAuthenticationStatus() {
/**
* Method that checks if [msgId] is a lockout error. A lockout error means that face
* authentication is locked out.
*/
- fun isLockoutError() = msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+ fun isLockoutError() =
+ msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT || msgId == FaceManager.FACE_ERROR_LOCKOUT
/**
* Method that checks if [msgId] is a cancellation error. This means that face authentication
@@ -64,4 +66,4 @@ data class ErrorAuthenticationStatus(
}
/** Face detection success message. */
-data class DetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean)
+data class FaceDetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
new file mode 100644
index 000000000000..7fc6016bf087
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.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.model
+
+import android.os.SystemClock.elapsedRealtime
+
+/**
+ * Fingerprint authentication status provided by
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository]
+ */
+sealed class FingerprintAuthenticationStatus
+
+/** Fingerprint authentication success status. */
+data class SuccessFingerprintAuthenticationStatus(
+ val userId: Int,
+ val isStrongBiometric: Boolean,
+) : FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication help message. */
+data class HelpFingerprintAuthenticationStatus(
+ val msgId: Int,
+ val msg: String?,
+) : FingerprintAuthenticationStatus()
+
+/** Fingerprint acquired message. */
+data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) :
+ FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication failed message. */
+object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication error message */
+data class ErrorFingerprintAuthenticationStatus(
+ val msgId: Int,
+ val msg: String? = null,
+ // present to break equality check if the same error occurs repeatedly.
+ val createdAt: Long = elapsedRealtime(),
+) : FingerprintAuthenticationStatus()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt
new file mode 100644
index 000000000000..80a1b75c4350
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.ScreenLifecycle
+
+/** Model device screen lifecycle states. */
+data class ScreenModel(
+ val state: ScreenState,
+) {
+ companion object {
+ fun fromScreenLifecycle(screenLifecycle: ScreenLifecycle): ScreenModel {
+ return ScreenModel(ScreenState.fromScreenLifecycleInt(screenLifecycle.getScreenState()))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt
new file mode 100644
index 000000000000..fe5d9355a6fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.ScreenLifecycle
+
+enum class ScreenState {
+ /** Screen is fully off. */
+ SCREEN_OFF,
+ /** Signal that the screen is turning on. */
+ SCREEN_TURNING_ON,
+ /** Screen is fully on. */
+ SCREEN_ON,
+ /** Signal that the screen is turning off. */
+ SCREEN_TURNING_OFF;
+
+ companion object {
+ fun fromScreenLifecycleInt(value: Int): ScreenState {
+ return when (value) {
+ ScreenLifecycle.SCREEN_OFF -> SCREEN_OFF
+ ScreenLifecycle.SCREEN_TURNING_ON -> SCREEN_TURNING_ON
+ ScreenLifecycle.SCREEN_ON -> SCREEN_ON
+ ScreenLifecycle.SCREEN_TURNING_OFF -> SCREEN_TURNING_OFF
+ else -> throw IllegalArgumentException("Invalid screen value: $value")
+ }
+ }
+ }
+}
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 728dd3911663..9872d97021fa 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
@@ -37,6 +37,7 @@ object UdfpsAodFingerprintViewBinder {
view: LottieAnimationView,
viewModel: UdfpsAodViewModel,
) {
+ view.alpha = 0f
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
index 26ef4685d286..0113628c30ca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
@@ -38,6 +38,7 @@ object UdfpsBackgroundViewBinder {
view: ImageView,
viewModel: BackgroundViewModel,
) {
+ view.alpha = 0f
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
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 0ab8e52fb6c7..bab04f234b3f 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
@@ -42,6 +42,7 @@ object UdfpsFingerprintViewBinder {
view: LottieAnimationView,
viewModel: FingerprintViewModel,
) {
+ view.alpha = 0f
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 68cdfb6d5865..373f70582612 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,7 +4,7 @@ import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
@@ -240,7 +240,7 @@ constructor(
)
}
- fun hardwareError(errorStatus: ErrorAuthenticationStatus) {
+ fun hardwareError(errorStatus: ErrorFaceAuthenticationStatus) {
logBuffer.log(
TAG,
DEBUG,
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 5aa5feeedcf1..f9324a95c1e5 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
@@ -16,19 +16,25 @@
package com.android.systemui.scene.ui.view
+import android.view.Gravity
+import android.view.View
import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.lifecycle.repeatWhenAttached
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 java.time.Instant
import kotlinx.coroutines.launch
object SceneWindowRootViewBinder {
@@ -77,6 +83,9 @@ object SceneWindowRootViewBinder {
)
)
+ val legacyView = view.requireViewById<View>(R.id.legacy_window_root)
+ view.addView(createVisibilityToggleView(legacyView))
+
launch {
viewModel.isVisible.collect { isVisible ->
onVisibilityChangedInternal(isVisible)
@@ -89,4 +98,28 @@ object SceneWindowRootViewBinder {
}
}
}
+
+ private var clickCount = 0
+ private var lastClick = Instant.now()
+
+ /**
+ * A temporary UI to toggle on/off the visibility of the given [otherView]. It is toggled by
+ * tapping 5 times in quick succession on the device camera (top center).
+ */
+ // TODO(b/291321285): Remove this when the Flexiglass UI is mature enough to turn off legacy
+ // SysUI altogether.
+ private fun createVisibilityToggleView(otherView: View): View {
+ val toggleView = View(otherView.context)
+ toggleView.layoutParams = FrameLayout.LayoutParams(200, 200, Gravity.CENTER_HORIZONTAL)
+ toggleView.setOnClickListener {
+ val now = Instant.now()
+ clickCount = if (now.minusSeconds(2) > lastClick) 1 else clickCount + 1
+ if (clickCount == 5) {
+ otherView.isVisible = !otherView.isVisible
+ clickCount = 0
+ }
+ lastClick = now
+ }
+ return toggleView
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index bb2be66cffda..b328c5564590 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -543,7 +543,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
state.forceUserActivity,
state.launchingActivityFromNotification,
state.mediaBackdropShowing,
- state.wallpaperSupportsAmbientMode,
state.windowNotTouchable,
state.componentsForcingTopUi,
state.forceOpenTokens,
@@ -734,12 +733,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
apply(mCurrentState);
}
- @Override
- public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
- mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
- apply(mCurrentState);
- }
-
/**
* @param state The {@link StatusBarStateController} of the status bar.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index 7812f07fc59c..d25294343d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -46,7 +46,6 @@ class NotificationShadeWindowState(
@JvmField var forceUserActivity: Boolean = false,
@JvmField var launchingActivityFromNotification: Boolean = false,
@JvmField var mediaBackdropShowing: Boolean = false,
- @JvmField var wallpaperSupportsAmbientMode: Boolean = false,
@JvmField var windowNotTouchable: Boolean = false,
@JvmField var componentsForcingTopUi: MutableSet<String> = mutableSetOf(),
@JvmField var forceOpenTokens: MutableSet<Any> = mutableSetOf(),
@@ -84,7 +83,6 @@ class NotificationShadeWindowState(
forceUserActivity.toString(),
launchingActivityFromNotification.toString(),
mediaBackdropShowing.toString(),
- wallpaperSupportsAmbientMode.toString(),
windowNotTouchable.toString(),
componentsForcingTopUi.toString(),
forceOpenTokens.toString(),
@@ -124,7 +122,6 @@ class NotificationShadeWindowState(
forceUserActivity: Boolean,
launchingActivity: Boolean,
backdropShowing: Boolean,
- wallpaperSupportsAmbientMode: Boolean,
notTouchable: Boolean,
componentsForcingTopUi: MutableSet<String>,
forceOpenTokens: MutableSet<Any>,
@@ -153,7 +150,6 @@ class NotificationShadeWindowState(
this.forceUserActivity = forceUserActivity
this.launchingActivityFromNotification = launchingActivity
this.mediaBackdropShowing = backdropShowing
- this.wallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode
this.windowNotTouchable = notTouchable
this.componentsForcingTopUi.clear()
this.componentsForcingTopUi.addAll(componentsForcingTopUi)
@@ -200,7 +196,6 @@ class NotificationShadeWindowState(
"forceUserActivity",
"launchingActivity",
"backdropShowing",
- "wallpaperSupportsAmbientMode",
"notTouchable",
"componentsForcingTopUi",
"forceOpenTokens",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index 4ec5f46e7771..7a989cfe227a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar;
import android.view.View;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -52,7 +51,6 @@ public class LegacyNotificationShelfControllerImpl implements NotificationShelfC
mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
- mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 47a4641bcdd9..5ac542b3530f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -112,9 +112,6 @@ public interface NotificationShadeWindowController extends RemoteInputController
/** Sets the state of whether heads up is showing or not. */
default void setHeadsUpShowing(boolean showing) {}
- /** Sets whether the wallpaper supports ambient mode or not. */
- default void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {}
-
/** Gets whether the wallpaper is showing or not. */
default boolean isShowingWallpaper() {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 25a1dc6322ba..3f37c60bee8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -24,7 +24,6 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -40,6 +39,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -95,8 +96,10 @@ public class NotificationShelf extends ActivatableNotificationView implements St
private float mCornerAnimationDistance;
private NotificationShelfController mController;
private float mActualWidth = -1;
- private boolean mSensitiveRevealAnimEnabled;
- private boolean mShelfRefactorFlagEnabled;
+ private final ViewRefactorFlag mSensitiveRevealAnim =
+ new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM);
+ private final ViewRefactorFlag mShelfRefactor =
+ new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR);
private boolean mCanModifyColorOfNotifications;
private boolean mCanInteract;
private NotificationStackScrollLayout mHostLayout;
@@ -130,7 +133,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -140,7 +143,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
NotificationRoundnessManager roundnessManager) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mAmbientState = ambientState;
mHostLayout = hostLayout;
mRoundnessManager = roundnessManager;
@@ -268,7 +271,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
- if (mSensitiveRevealAnimEnabled && viewState.hidden) {
+ if (mSensitiveRevealAnim.isEnabled() && viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
// padding), such that when it appears animated, it will smoothly move in from the
// bottom, without jump cutting any notifications
@@ -279,7 +282,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getSpeedBumpIndex() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getSpeedBumpIndex();
} else {
return mHostLayoutController.getSpeedBumpIndex();
@@ -413,7 +416,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
expandingAnimated, isLastChild, shelfClipStart);
// TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
- if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf())
+ if ((!mSensitiveRevealAnim.isEnabled() && ((isLastChild && !child.isInShelf())
|| backgroundForceHidden)) || aboveShelf) {
notificationClipEnd = shelfStart + getIntrinsicHeight();
} else {
@@ -462,7 +465,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
// if the shelf is visible, but if the shelf is hidden, it causes incorrect curling.
// notificationClipEnd handles the discrepancy between a visible and hidden shelf,
// so we use that when on the keyguard (and while animating away) to reduce curling.
- final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled
+ final float keyguardSafeShelfStart = !mSensitiveRevealAnim.isEnabled()
&& mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart;
updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart);
}
@@ -504,7 +507,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private ExpandableView getHostLayoutChildAt(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return (ExpandableView) mHostLayout.getChildAt(index);
} else {
return mHostLayoutController.getChildAt(index);
@@ -512,7 +515,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getHostLayoutChildCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getChildCount();
} else {
return mHostLayoutController.getChildCount();
@@ -520,7 +523,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private boolean canModifyColorOfNotifications() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded();
} else {
return mController.canModifyColorOfNotifications();
@@ -583,7 +586,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
- if (!mShelfRefactorFlagEnabled) {
+ if (!mShelfRefactor.isEnabled()) {
return mHostLayoutController.isViewAffectedBySwipe(expandableView);
} else {
return mRoundnessManager.isViewAffectedBySwipe(expandableView);
@@ -607,7 +610,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private View getHostLayoutTransientView(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientView(index);
} else {
return mHostLayoutController.getTransientView(index);
@@ -615,7 +618,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getHostLayoutTransientViewCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientViewCount();
} else {
return mHostLayoutController.getTransientViewCount();
@@ -961,7 +964,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
@Override
public void onStateChanged(int newState) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mStatusBarState = newState;
updateInteractiveness();
}
@@ -975,7 +978,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private boolean canInteract() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanInteract;
} else {
return mStatusBarState == StatusBarState.KEYGUARD;
@@ -1018,32 +1021,18 @@ public class NotificationShelf extends ActivatableNotificationView implements St
return false;
}
- private void assertRefactorFlagDisabled() {
- if (mShelfRefactorFlagEnabled) {
- NotificationShelfController.throwIllegalFlagStateError(false);
- }
- }
-
- private boolean checkRefactorFlagEnabled() {
- if (!mShelfRefactorFlagEnabled) {
- Log.wtf(TAG,
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled.");
- }
- return mShelfRefactorFlagEnabled;
- }
-
public void setController(NotificationShelfController notificationShelfController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mController = notificationShelfController;
}
public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanModifyColorOfNotifications = canModifyColorOfNotifications;
}
public void setCanInteract(boolean canInteract) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanInteract = canInteract;
updateInteractiveness();
}
@@ -1053,27 +1042,15 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getIndexOfViewInHostLayout(ExpandableView child) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.indexOfChild(child);
} else {
return mHostLayoutController.indexOfChild(child);
}
}
- /**
- * Set whether the sensitive reveal animation feature flag is enabled
- * @param enabled true if enabled
- */
- public void setSensitiveRevealAnimEnabled(boolean enabled) {
- mSensitiveRevealAnimEnabled = enabled;
- }
-
- public void setRefactorFlagEnabled(boolean enabled) {
- mShelfRefactorFlagEnabled = enabled;
- }
-
public void requestRoundnessResetFor(ExpandableView child) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
child.requestRoundnessReset(SHELF_SCROLL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index 1619ddaac85c..8a3e21756c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -16,11 +16,8 @@
package com.android.systemui.statusbar
-import android.util.Log
import android.view.View
import android.view.View.OnClickListener
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -49,29 +46,4 @@ interface NotificationShelfController {
/** @see View.setOnClickListener */
fun setOnClickListener(listener: OnClickListener)
-
- companion object {
- @JvmStatic
- fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- throwIllegalFlagStateError(expected = false)
- }
- }
-
- @JvmStatic
- fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean =
- featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled ->
- if (!enabled) {
- Log.wtf("NotifShelf", getErrorMessage(expected = true))
- }
- }
-
- @JvmStatic
- fun throwIllegalFlagStateError(expected: Boolean): Nothing =
- error(getErrorMessage(expected))
-
- private fun getErrorMessage(expected: Boolean): String =
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " +
- if (expected) "disabled" else "enabled"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 1cf9c1e1f299..1c5aa3cce266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,10 @@ package com.android.systemui.statusbar.notification
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ViewRefactorFlag
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -46,14 +46,14 @@ interface Roundable {
@JvmDefault
val topCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
@JvmDefault
val bottomCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
@@ -335,13 +335,12 @@ constructor(
internal val targetView: View,
private val roundable: Roundable,
maxRadius: Float,
- private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java)
+ featureFlags: FeatureFlags? = null
) {
internal var maxRadius = maxRadius
private set
- internal val newHeadsUpAnimFlagEnabled
- get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS)
+ internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS)
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 189608072ec7..9ecf50ee4f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -176,8 +175,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
entry.setRow(row);
mNotifBindPipeline.manageRow(entry, row);
mPresenter.onBindRow(row);
- row.setInlineReplyAnimationFlagEnabled(
- mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
}
/**
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 908c11a1d076..36a8e9833d39 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
@@ -566,7 +566,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public float getTopCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getTopCornerRadius();
}
@@ -576,7 +576,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public float getBottomCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getBottomCornerRadius();
}
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 b34c28163abb..42b99a1dc68c 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
@@ -77,6 +77,7 @@ import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -275,7 +276,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnFeedbackClickListener;
private Path mExpandingClipPath;
- private boolean mIsInlineReplyAnimationFlagEnabled = false;
+ private final ViewRefactorFlag mInlineReplyAnimation =
+ new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -3121,10 +3123,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
}
- public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) {
- mIsInlineReplyAnimationFlagEnabled = isEnabled;
- }
-
@Override
public void setActualHeight(int height, boolean notifyListeners) {
boolean changed = height != getActualHeight();
@@ -3144,7 +3142,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
int contentHeight = Math.max(getMinHeight(), height);
for (NotificationContentView l : mLayouts) {
- if (mIsInlineReplyAnimationFlagEnabled) {
+ if (mInlineReplyAnimation.isEnabled()) {
l.setContentHeight(height);
} else {
l.setContentHeight(contentHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 7f23c1b89b51..c8f13a6302cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,10 +28,9 @@ import android.util.IndentingPrintWriter;
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -50,7 +49,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- private final FeatureFlags mFeatureFlags;
+ protected final ViewRefactorFlag mImprovedHunAnimation =
+ new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS);
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -126,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
- if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) {
+ if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
float overShoot = topRadius + bottomRadius - height;
float currentTopRoundness = getTopRoundness();
float currentBottomRoundness = getBottomRoundness();
@@ -167,7 +167,6 @@ public abstract class ExpandableOutlineView extends ExpandableView {
super(context, attrs);
setOutlineProvider(mProvider);
initDimens();
- mFeatureFlags = Dependency.get(FeatureFlags.class);
}
@Override
@@ -376,8 +375,4 @@ public abstract class ExpandableOutlineView extends ExpandableView {
});
}
- // TODO(b/290365128) replace with ViewRefactorFlag
- protected boolean isNewHeadsUpAnimFlagEnabled() {
- return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS);
- }
}
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 23a58d252ba6..22a87a7c9432 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
@@ -21,7 +21,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
@@ -66,7 +65,7 @@ class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() :
override fun setOnClickListener(listener: View.OnClickListener) = unsupported
private val unsupported: Nothing
- get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
+ get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
}
/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
@@ -80,8 +79,6 @@ object NotificationShelfViewBinder {
) {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
- setRefactorFlagEnabled(true)
- setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
// TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
notificationIconAreaController.setShelfIcons(shelfIcons)
repeatWhenAttached {
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 b0f3f598cb91..95e74f210c5d 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
@@ -29,7 +29,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -57,7 +56,6 @@ public class AmbientState implements Dumpable {
private final SectionProvider mSectionProvider;
private final BypassController mBypassController;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- private final FeatureFlags mFeatureFlags;
/**
* Used to read bouncer states.
*/
@@ -261,13 +259,12 @@ public class AmbientState implements Dumpable {
@NonNull SectionProvider sectionProvider,
@NonNull BypassController bypassController,
@Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator,
- @NonNull FeatureFlags featureFlags) {
+ @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator
+ ) {
mSectionProvider = sectionProvider;
mBypassController = bypassController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
- mFeatureFlags = featureFlags;
reload(context);
dumpManager.registerDumpable(this);
}
@@ -753,10 +750,6 @@ public class AmbientState implements Dumpable {
return mLargeScreenShadeInterpolator;
}
- public FeatureFlags getFeatureFlags() {
- return mFeatureFlags;
- }
-
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("mTopPadding=" + mTopPadding);
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 c1ceb3ce5ab6..d71bc2fd6470 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
@@ -89,6 +89,7 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.shade.ShadeController;
@@ -198,7 +199,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
- private boolean mAnimatedInsets;
+ private final ViewRefactorFlag mAnimatedInsets;
+ private final ViewRefactorFlag mShelfRefactor;
private int mContentHeight;
private float mIntrinsicContentHeight;
@@ -621,7 +623,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
- setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
+ mAnimatedInsets =
+ new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -660,7 +664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
setWindowInsetsAnimationCallback(mInsetsCallback);
}
}
@@ -730,11 +734,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@VisibleForTesting
- void setAnimatedInsetsEnabled(boolean enabled) {
- mAnimatedInsets = enabled;
- }
-
- @VisibleForTesting
public void updateFooter() {
if (mFooterView == null) {
return;
@@ -1773,7 +1772,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return;
}
mForcedScroll = v;
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
updateForcedScroll();
} else {
scrollTo(v);
@@ -1822,7 +1821,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
}
mWaterfallTopInset = 0;
@@ -1830,11 +1829,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (cutout != null) {
mWaterfallTopInset = cutout.getWaterfallInsets().top;
}
- if (mAnimatedInsets && !mIsInsetAnimationRunning) {
+ if (mAnimatedInsets.isEnabled() && !mIsInsetAnimationRunning) {
// update bottom inset e.g. after rotation
updateBottomInset(insets);
}
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
int range = getScrollRange();
if (mOwnScrollY > range) {
// HACK: We're repeatedly getting staggered insets here while the IME is
@@ -2714,7 +2713,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
mOnNotificationRemovedListener = listener;
}
@@ -2727,7 +2726,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (!mChildTransferInProgress) {
onViewRemovedInternal(expandableView, this);
}
- if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ if (mShelfRefactor.isEnabled()) {
mShelf.requestRoundnessResetFor(expandableView);
} else {
if (mOnNotificationRemovedListener != null) {
@@ -4943,18 +4942,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Nullable
public ExpandableView getShelf() {
- if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) {
- return mShelf;
- } else {
- return null;
- }
+ if (!mShelfRefactor.expectEnabled()) return null;
+ return mShelf;
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(
- mAmbientState.getFeatureFlags())) {
- return;
- }
+ if (!mShelfRefactor.expectEnabled()) return;
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
@@ -4968,7 +4961,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
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 ef7375aa690b..4668aa433533 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
@@ -65,6 +65,7 @@ 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;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -205,6 +206,7 @@ public class NotificationStackScrollLayoutController {
private boolean mIsInTransitionToAod = false;
private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -718,6 +720,7 @@ public class NotificationStackScrollLayoutController {
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
mDismissibilityProvider = dismissibilityProvider;
@@ -1432,7 +1435,7 @@ public class NotificationStackScrollLayoutController {
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mView.setShelfController(notificationShelfController);
}
@@ -1645,12 +1648,12 @@ public class NotificationStackScrollLayoutController {
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mView.setShelf(shelf);
}
public int getShelfHeight() {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (!mShelfRefactor.expectEnabled()) {
return 0;
}
ExpandableView shelf = mView.getShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 2b9c3d33e9b8..acd6e49fe8c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -259,8 +259,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {
void readyForKeyguardDone();
- void setLockscreenUser(int newUserId);
-
void showKeyguard();
boolean hideKeyguard();
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 5fb729cb26f1..8361d6ba1801 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -51,7 +51,6 @@ import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.TaskInfo;
import android.app.UiModeManager;
-import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -980,16 +979,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
createAndAddWindows(result);
- if (mWallpaperSupported) {
- // Make sure we always have the most current wallpaper info.
- IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
- mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter,
- null /* handler */, UserHandle.ALL);
- mWallpaperChangedReceiver.onReceive(mContext, null);
- } else if (DEBUG) {
- Log.v(TAG, "start(): no wallpaper service ");
- }
-
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
@@ -2164,18 +2153,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
};
/**
- * Notify the shade controller that the current user changed
- *
- * @param newUserId userId of the new user
- */
- @Override
- public void setLockscreenUser(int newUserId) {
- if (mWallpaperSupported) {
- mWallpaperChangedReceiver.onReceive(mContext, null);
- }
- }
-
- /**
* Reload some of our resources when the configuration changes.
*
* We don't reload everything when the configuration changes -- we probably
@@ -3549,33 +3526,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
};
- /**
- * @deprecated See {@link com.android.systemui.wallpapers.data.repository.WallpaperRepository}
- * instead.
- */
- private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mWallpaperSupported) {
- // Receiver should not have been registered at all...
- Log.wtf(TAG, "WallpaperManager not supported");
- return;
- }
- WallpaperInfo info = mWallpaperManager.getWallpaperInfoForUser(
- mUserTracker.getUserId());
- mWallpaperController.onWallpaperInfoUpdated(info);
-
- final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
- // If WallpaperInfo is null, it must be ImageWallpaper.
- final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info != null && info.supportsAmbientMode());
-
- mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- }
- };
-
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onConfigChanged(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 7312db6595e5..ed9722e04da0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -311,6 +311,7 @@ public final class DozeServiceHost implements DozeHost {
@Override
public void dozeTimeTick() {
+ mDozeInteractor.dozeTimeTick();
mNotificationPanel.dozeTimeTick();
mAuthController.dozeTimeTick();
if (mAmbientIndicationContainer instanceof DozeReceiver) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e18c9d86d74b..0bf0f4b504b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,8 @@ 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.flags.ViewRefactorFlag;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -88,7 +90,7 @@ public class NotificationIconAreaController implements
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
- private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private int mAodIconAppearTranslation;
@@ -120,12 +122,13 @@ public class NotificationIconAreaController implements
Optional<Bubbles> bubblesOptional,
DemoModeController demoModeController,
DarkIconDispatcher darkIconDispatcher,
- FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController,
+ FeatureFlags featureFlags,
+ StatusBarWindowController statusBarWindowController,
ScreenOffAnimationController screenOffAnimationController) {
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
mDozeParameters = dozeParameters;
@@ -179,12 +182,12 @@ public class NotificationIconAreaController implements
}
public void setupShelf(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mShelfIcons = notificationShelfController.getShelfIcons();
}
public void setShelfIcons(NotificationIconContainer icons) {
- if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (mShelfRefactor.expectEnabled()) {
mShelfIcons = icons;
}
}
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 e63fecd33383..ad8530df0523 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -79,7 +79,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final CentralSurfaces mCentralSurfaces;
private final NotificationsInteractor mNotificationsInteractor;
private final NotificationStackScrollLayoutController mNsslController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -107,7 +106,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- CentralSurfaces centralSurfaces,
NotificationsInteractor notificationsInteractor,
LockscreenShadeTransitionController shadeTransitionController,
PowerInteractor powerInteractor,
@@ -128,8 +126,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfaces = centralSurfaces;
mNotificationsInteractor = notificationsInteractor;
mNsslController = stackScrollerController;
mShadeTransitionController = shadeTransitionController;
@@ -208,7 +204,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
// End old BaseStatusBar.userSwitched
mCommandQueue.animateCollapsePanels();
mMediaManager.clearCurrentMediaNotification();
- mCentralSurfaces.setLockscreenUser(newUserId);
updateMediaMetaData(true, false);
}
@@ -284,7 +279,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mCentralSurfaces.isOccluded()) {
+ if (mKeyguardStateController.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 27cc64f9a8e8..0e99c67a0d80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -19,12 +19,10 @@ package com.android.systemui.statusbar.pipeline.dagger
import android.net.wifi.WifiManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,6 +44,8 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinderImpl
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
@@ -58,9 +58,9 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
-import kotlinx.coroutines.flow.Flow
import java.util.function.Supplier
import javax.inject.Named
+import kotlinx.coroutines.flow.Flow
@Module
abstract class StatusBarPipelineModule {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 5d09e064604a..a501e87902a8 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -20,6 +20,11 @@ import android.content.Intent;
import com.android.systemui.Dependency;
+/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ */
+@Deprecated
public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8cfe2eac3d33..ccc0a79d2cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -56,7 +56,11 @@ import javax.inject.Inject;
/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ * This class will interact with SecureSettings using the main looper.
*/
+@Deprecated
@SysUISingleton
public class TunerServiceImpl extends TunerService {
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index db2aca873d0c..65a02184f96d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -16,32 +16,34 @@
package com.android.systemui.util
-import android.app.WallpaperInfo
import android.app.WallpaperManager
import android.util.Log
import android.view.View
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
import javax.inject.Inject
import kotlin.math.max
private const val TAG = "WallpaperController"
+/**
+ * Controller for wallpaper-related logic.
+ *
+ * Note: New logic should be added to [WallpaperRepository], not this class.
+ */
@SysUISingleton
-class WallpaperController @Inject constructor(private val wallpaperManager: WallpaperManager) {
+class WallpaperController @Inject constructor(
+ private val wallpaperManager: WallpaperManager,
+ private val wallpaperRepository: WallpaperRepository,
+) {
var rootView: View? = null
private var notificationShadeZoomOut: Float = 0f
private var unfoldTransitionZoomOut: Float = 0f
- private var wallpaperInfo: WallpaperInfo? = null
-
- fun onWallpaperInfoUpdated(wallpaperInfo: WallpaperInfo?) {
- this.wallpaperInfo = wallpaperInfo
- }
-
private val shouldUseDefaultUnfoldTransition: Boolean
- get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition()
+ get() = wallpaperRepository.wallpaperInfo.value?.shouldUseDefaultUnfoldTransition()
?: true
fun setNotificationShadeZoom(zoomOut: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index a64058968581..b45b8cd15bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -16,9 +16,11 @@
package com.android.systemui.wallpapers.data.repository
+import android.app.WallpaperInfo
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/**
@@ -29,5 +31,6 @@ import kotlinx.coroutines.flow.asStateFlow
*/
@SysUISingleton
class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
+ override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 48895ffcacb9..b8f95832b852 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.wallpapers.data.repository
+import android.app.WallpaperInfo
import android.app.WallpaperManager
import android.content.Context
import android.content.Intent
@@ -36,11 +37,15 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
/** A repository storing information about the current wallpaper. */
interface WallpaperRepository {
+ /** Emits the current user's current wallpaper. */
+ val wallpaperInfo: StateFlow<WallpaperInfo?>
+
/** Emits true if the current user's current wallpaper supports ambient mode. */
val wallpaperSupportsAmbientMode: StateFlow<Boolean>
}
@@ -78,28 +83,35 @@ constructor(
// Only update the wallpaper status once the user selection has finished.
.filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
- override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+ override val wallpaperInfo: StateFlow<WallpaperInfo?> =
if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) {
- MutableStateFlow(false).asStateFlow()
+ MutableStateFlow(null).asStateFlow()
} else {
combine(wallpaperChanged, selectedUser) { _, selectedUser ->
- doesWallpaperSupportAmbientMode(selectedUser)
+ getWallpaper(selectedUser)
}
.stateIn(
scope,
// Always be listening for wallpaper changes.
SharingStarted.Eagerly,
- initialValue =
- doesWallpaperSupportAmbientMode(userRepository.selectedUser.value),
+ initialValue = getWallpaper(userRepository.selectedUser.value),
)
}
- private fun doesWallpaperSupportAmbientMode(selectedUser: SelectedUserModel): Boolean {
- return wallpaperManager
- .getWallpaperInfoForUser(
- selectedUser.userInfo.id,
+ override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+ wallpaperInfo
+ .map {
+ // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
+ it?.supportsAmbientMode() == true
+ }
+ .stateIn(
+ scope,
+ // Always be listening for wallpaper changes.
+ SharingStarted.Eagerly,
+ initialValue = wallpaperInfo.value?.supportsAmbientMode() == true,
)
- // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
- ?.supportsAmbientMode() == true
+
+ private fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
+ return wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 5d75428b8fb4..cb182297eae1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -76,7 +76,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
- @Mock private lateinit var mPostureController: DevicePostureController
+ @Mock private lateinit var mPostureController: DevicePostureController
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
private lateinit var fakeFeatureFlags: FakeFeatureFlags
@@ -119,7 +119,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController.onViewAttached()
- assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+ assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
}
@Test
@@ -131,15 +131,20 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController.onViewAttached()
// Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
- assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+ assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
// Simulate posture change to state DEVICE_POSTURE_OPENED with callback
verify(mPostureController).addCallback(postureCallbackCaptor.capture())
val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value
postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
- // Verify view is now in posture state DEVICE_POSTURE_OPENED
- assertThat(getPatternTopGuideline()).isNotEqualTo(getExpectedTopGuideline())
+ // Simulate posture change to same state with callback
+ assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+
+ postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ // Verify view is still in posture state DEVICE_POSTURE_OPENED
+ assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
}
private fun getPatternTopGuideline(): Float {
@@ -150,7 +155,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
}
- private fun getExpectedTopGuideline(): Float {
+ private fun getHalfOpenedBouncerHeightRatio(): Float {
return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d256ee163877..4dc7652f83cf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -19,6 +19,8 @@ package com.android.keyguard
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
@@ -32,6 +34,8 @@ import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,7 +55,10 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
- @Mock private lateinit var keyguardPinView: KeyguardPINView
+
+ private lateinit var objectKeyguardPINView: KeyguardPINView
+
+ @Mock private lateinit var mockKeyguardPinView: KeyguardPINView
@Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
@@ -83,64 +90,73 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Mock lateinit var deleteButton: NumPadButton
@Mock lateinit var enterButton: View
- private lateinit var pinViewController: KeyguardPinViewController
-
@Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+ Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
.thenReturn(keyguardMessageArea)
Mockito.`when`(
keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
)
.thenReturn(keyguardMessageAreaController)
- `when`(keyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
- `when`(keyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
+ `when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
+ `when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
.thenReturn(passwordTextView)
- `when`(keyguardPinView.resources).thenReturn(context.resources)
- `when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
+ `when`(mockKeyguardPinView.resources).thenReturn(context.resources)
+ `when`(mockKeyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
.thenReturn(deleteButton)
- `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
+ `when`(mockKeyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
// For posture tests:
- `when`(keyguardPinView.buttons).thenReturn(arrayOf())
+ `when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
- pinViewController =
- KeyguardPinViewController(
- keyguardPinView,
- keyguardUpdateMonitor,
- securityMode,
- lockPatternUtils,
- mKeyguardSecurityCallback,
- keyguardMessageAreaControllerFactory,
- mLatencyTracker,
- liftToActivateListener,
- mEmergencyButtonController,
- falsingCollector,
- postureController,
- featureFlags
- )
+ objectKeyguardPINView =
+ View.inflate(mContext, R.layout.keyguard_pin_view, null)
+ .findViewById(R.id.keyguard_pin_view) as KeyguardPINView
+ }
+
+ private fun constructPinViewController(
+ mKeyguardPinView: KeyguardPINView
+ ): KeyguardPinViewController {
+ return KeyguardPinViewController(
+ mKeyguardPinView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ mKeyguardSecurityCallback,
+ keyguardMessageAreaControllerFactory,
+ mLatencyTracker,
+ liftToActivateListener,
+ mEmergencyButtonController,
+ falsingCollector,
+ postureController,
+ featureFlags
+ )
}
@Test
- fun onViewAttached_deviceHalfFolded_propagatedToPinView() {
- `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+ fun onViewAttached_deviceHalfFolded_propagatedToPatternView() {
+ val pinViewController = constructPinViewController(objectKeyguardPINView)
+ overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
+ whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
pinViewController.onViewAttached()
- verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
+ assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
}
@Test
- fun onDevicePostureChanged_deviceHalfFolded_propagatedToPinView() {
- `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+ fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() {
+ val pinViewController = constructPinViewController(objectKeyguardPINView)
+ overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
- // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
+ whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
pinViewController.onViewAttached()
- verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
+ // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
+ assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
// Simulate posture change to state DEVICE_POSTURE_OPENED with callback
verify(postureController).addCallback(postureCallbackCaptor.capture())
@@ -148,31 +164,57 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
// Verify view is now in posture state DEVICE_POSTURE_OPENED
- verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_OPENED)
+ assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+
+ // Simulate posture change to same state with callback
+ postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+ // Verify view is still in posture state DEVICE_POSTURE_OPENED
+ assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+ }
+
+ private fun getPinTopGuideline(): Float {
+ val cs = ConstraintSet()
+ val container = objectKeyguardPINView.findViewById(R.id.pin_container) as ConstraintLayout
+ cs.clone(container)
+ return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
+ }
+
+ private fun getHalfOpenedBouncerHeightRatio(): Float {
+ return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
}
@Test
fun startAppearAnimation() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
+
pinViewController.startAppearAnimation()
+
verify(keyguardMessageAreaController)
.setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
}
@Test
fun startAppearAnimation_withExistingMessage() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+
pinViewController.startAppearAnimation()
+
verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
}
@Test
fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+ `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
`when`(passwordTextView.text).thenReturn("")
pinViewController.startAppearAnimation()
+
verify(deleteButton).visibility = View.INVISIBLE
verify(enterButton).visibility = View.INVISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -181,12 +223,15 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test
fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
`when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+ `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
`when`(passwordTextView.text).thenReturn("")
pinViewController.startAppearAnimation()
+
verify(deleteButton).visibility = View.VISIBLE
verify(enterButton).visibility = View.VISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -195,7 +240,10 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Test
fun handleLockout_readsNumberOfErrorAttempts() {
+ val pinViewController = constructPinViewController(mockKeyguardPinView)
+
pinViewController.handleAttemptLockout(0)
+
verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 6decb88ee148..5867a40c78fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,8 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -43,12 +45,14 @@ import org.junit.runner.RunWith;
@RunWithLooper
public class ExpandHelperTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private ExpandableNotificationRow mRow;
private ExpandHelper mExpandHelper;
private ExpandHelper.Callback mCallback;
@Before
public void setUp() throws Exception {
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
allowTestableLooperAsMainThread();
@@ -56,7 +60,8 @@ public class ExpandHelperTest extends SysuiTestCase {
NotificationTestHelper helper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = helper.createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
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 eaa31ac1d157..ecc776b98c6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -48,6 +48,7 @@ import android.view.WindowMetrics
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
@@ -64,7 +65,6 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -153,8 +153,8 @@ class SideFpsControllerTest : SysuiTestCase() {
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
FakeBiometricSettingsRepository(),
- FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
+ mock(KeyguardUpdateMonitor::class.java),
)
displayStateInteractor =
DisplayStateInteractorImpl(
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 9df06dc9e18f..8dfeb3bde0c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -34,7 +34,6 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -106,8 +105,8 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
mock(KeyguardStateController::class.java),
keyguardBouncerRepository,
mock(BiometricSettingsRepository::class.java),
- mock(DeviceEntryFingerprintAuthRepository::class.java),
mock(SystemClock::class.java),
+ mKeyguardUpdateMonitor,
)
return createUdfpsKeyguardViewController(
/* useModernBouncer */ true, /* useExpandedOverlay */
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 37b9ca49ef57..186df02536ea 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
@@ -18,6 +18,7 @@ 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.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
@@ -54,6 +55,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var bouncerLogger: TableLogBuffer
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Before
fun setup() {
@@ -72,8 +74,8 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
keyguardStateController,
bouncerRepository,
biometricSettingsRepository,
- deviceEntryFingerprintAuthRepository,
systemClock,
+ keyguardUpdateMonitor,
)
}
@@ -118,7 +120,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
@Test
fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
givenCanShowAlternateBouncer()
- deviceEntryFingerprintAuthRepository.setLockedOut(true)
+ whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
@@ -168,7 +170,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
biometricSettingsRepository.setFingerprintEnrolled(true)
biometricSettingsRepository.setStrongBiometricAllowed(true)
biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
- deviceEntryFingerprintAuthRepository.setLockedOut(false)
+ whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
whenever(keyguardStateController.isUnlocked).thenReturn(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
index 461ec653d819..40f0ed3626db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -28,10 +28,10 @@ import androidx.lifecycle.Observer;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.DreamLogger;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -57,8 +57,6 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
private FakeFeatureFlags mFeatureFlags;
@Mock
private Observer mObserver;
- @Mock
- private DreamLogger mLogger;
@Before
public void setUp() {
@@ -70,7 +68,7 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
mExecutor,
/* overlayEnabled= */ true,
mFeatureFlags,
- mLogger);
+ FakeLogBuffer.Factory.Companion.create());
mLiveData = new ComplicationCollectionLiveData(mStateController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index a00e5456b711..57307fc84b1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -9,6 +9,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.mockito.argumentCaptor
@@ -47,7 +48,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
@Mock private lateinit var stateController: DreamOverlayStateController
@Mock private lateinit var configController: ConfigurationController
@Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel
- @Mock private lateinit var logger: DreamLogger
+ private val logBuffer = FakeLogBuffer.Factory.create()
private lateinit var controller: DreamOverlayAnimationsController
@Before
@@ -66,7 +67,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
DREAM_IN_TRANSLATION_Y_DISTANCE,
DREAM_IN_TRANSLATION_Y_DURATION,
- logger
+ logBuffer
)
val mockView: View = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 2c1ebe4121af..44a78ac3bc96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -34,6 +34,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.Complication;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -58,8 +60,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Mock
private FeatureFlags mFeatureFlags;
- @Mock
- private DreamLogger mLogger;
+ private final LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create();
final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@@ -408,6 +409,11 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
}
private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) {
- return new DreamOverlayStateController(mExecutor, overlayEnabled, mFeatureFlags, mLogger);
+ return new DreamOverlayStateController(
+ mExecutor,
+ overlayEnabled,
+ mFeatureFlags,
+ mLogBuffer
+ );
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 5dc0e55632fd..4e74f451932b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -48,6 +48,8 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -113,8 +115,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
DreamOverlayStateController mDreamOverlayStateController;
@Mock
UserTracker mUserTracker;
- @Mock
- DreamLogger mLogger;
+
+ LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create();
@Captor
private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
@@ -149,7 +151,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
- mLogger);
+ mLogBuffer);
}
@Test
@@ -293,7 +295,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
- mLogger);
+ mLogBuffer);
controller.onViewAttached();
verify(mView, never()).showIcon(
eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
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 ddcbe7993697..00f67a32ff8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -78,6 +78,7 @@ import com.android.keyguard.KeyguardDisplayManager;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
@@ -115,9 +116,11 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import org.junit.After;
@@ -132,6 +135,7 @@ import org.mockito.MockitoAnnotations;
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.test.TestScope;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -139,6 +143,9 @@ import kotlinx.coroutines.flow.Flow;
public class KeyguardViewMediatorTest extends SysuiTestCase {
private KeyguardViewMediator mViewMediator;
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+
private @Mock UserTracker mUserTracker;
private @Mock DevicePolicyManager mDevicePolicyManager;
private @Mock LockPatternUtils mLockPatternUtils;
@@ -191,6 +198,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock SecureSettings mSecureSettings;
private @Mock AlarmManager mAlarmManager;
private FakeSystemClock mSystemClock;
+ private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
/** Most recent value passed to {@link KeyguardStateController#notifyKeyguardGoingAway}. */
private boolean mKeyguardGoingAway = false;
@@ -928,6 +936,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mKeyguardTransitions,
mInteractionJankMonitor,
mDreamOverlayStateController,
+ mJavaAdapter,
+ mWallpaperRepository,
() -> mShadeController,
() -> mNotificationShadeWindowController,
() -> mActivityLaunchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index f9070b37ca48..c6a2fa50b446 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -162,11 +162,11 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
@Test
fun convenienceBiometricAllowedChange() =
testScope.runTest {
+ overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
createBiometricSettingsRepository()
val convenienceBiometricAllowed =
collectLastValue(underTest.isNonStrongBiometricAllowed)
runCurrent()
-
onNonStrongAuthChanged(true, PRIMARY_USER_ID)
assertThat(convenienceBiometricAllowed()).isTrue()
@@ -175,6 +175,45 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
assertThat(convenienceBiometricAllowed()).isFalse()
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+ )
+ }
+
+ @Test
+ fun whenStrongAuthRequiredAfterBoot_nonStrongBiometricNotAllowed() =
+ testScope.runTest {
+ overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, true)
+ createBiometricSettingsRepository()
+
+ val convenienceBiometricAllowed =
+ collectLastValue(underTest.isNonStrongBiometricAllowed)
+ runCurrent()
+ onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+
+ assertThat(convenienceBiometricAllowed()).isFalse()
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+ )
+ }
+
+ @Test
+ fun whenStrongBiometricAuthIsNotAllowed_nonStrongBiometrics_alsoNotAllowed() =
+ testScope.runTest {
+ overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
+ createBiometricSettingsRepository()
+
+ val convenienceBiometricAllowed =
+ collectLastValue(underTest.isNonStrongBiometricAllowed)
+ runCurrent()
+ onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isTrue()
+
+ onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, PRIMARY_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isFalse()
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+ )
}
private fun onStrongAuthChanged(flags: Int, userId: Int) {
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 020c0b22ba27..47c662c3b4b0 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
@@ -36,6 +36,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
@@ -50,12 +51,12 @@ import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -113,6 +114,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var sessionTracker: SessionTracker
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Captor
private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
@@ -131,8 +133,8 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var testScope: TestScope
private lateinit var fakeUserRepository: FakeUserRepository
- private lateinit var authStatus: FlowValue<AuthenticationStatus?>
- private lateinit var detectStatus: FlowValue<DetectionStatus?>
+ private lateinit var authStatus: FlowValue<FaceAuthenticationStatus?>
+ private lateinit var detectStatus: FlowValue<FaceDetectionStatus?>
private lateinit var authRunning: FlowValue<Boolean?>
private lateinit var bypassEnabled: FlowValue<Boolean?>
private lateinit var lockedOut: FlowValue<Boolean?>
@@ -175,10 +177,10 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
AlternateBouncerInteractor(
bouncerRepository = bouncerRepository,
biometricSettingsRepository = biometricSettingsRepository,
- deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
systemClock = mock(SystemClock::class.java),
keyguardStateController = FakeKeyguardStateController(),
statusBarStateController = mock(StatusBarStateController::class.java),
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
)
bypassStateChangedListener =
@@ -262,7 +264,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
val successResult = successResult()
authenticationCallback.value.onAuthenticationSucceeded(successResult)
- assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+ assertThat(authStatus()).isEqualTo(SuccessFaceAuthenticationStatus(successResult))
assertThat(authenticated()).isTrue()
assertThat(authRunning()).isFalse()
assertThat(canFaceAuthRun()).isFalse()
@@ -383,7 +385,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
detectionCallback.value.onFaceDetected(1, 1, true)
- assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+ assertThat(detectStatus()).isEqualTo(FaceDetectionStatus(1, 1, true))
}
@Test
@@ -423,7 +425,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
FACE_ERROR_CANCELED,
"First auth attempt cancellation completed"
)
- val value = authStatus() as ErrorAuthenticationStatus
+ val value = authStatus() as ErrorFaceAuthenticationStatus
assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED)
assertThat(value.msg).isEqualTo("First auth attempt cancellation completed")
@@ -465,7 +467,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
- assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+ assertThat(authStatus()).isEqualTo(HelpFaceAuthenticationStatus(9, "help msg"))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 264328b04227..def016ad8381 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -26,7 +26,11 @@ import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,7 +53,6 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var authController: AuthController
@Captor
private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -68,7 +71,6 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
authController,
keyguardUpdateMonitor,
testScope.backgroundScope,
- dumpManager,
)
}
@@ -177,4 +179,129 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
}
+
+ @Test
+ fun onFingerprintSuccess_successAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAuthenticated(
+ 0,
+ BiometricSourceType.FINGERPRINT,
+ true,
+ )
+
+ val status = authenticationStatus as SuccessFingerprintAuthenticationStatus
+ assertThat(status.userId).isEqualTo(0)
+ assertThat(status.isStrongBiometric).isEqualTo(true)
+ }
+
+ @Test
+ fun onFingerprintFailed_failedAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAuthFailed(
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ assertThat(authenticationStatus)
+ .isInstanceOf(FailFingerprintAuthenticationStatus::class.java)
+ }
+
+ @Test
+ fun onFingerprintError_errorAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricError(
+ 1,
+ "test_string",
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ val status = authenticationStatus as ErrorFingerprintAuthenticationStatus
+ assertThat(status.msgId).isEqualTo(1)
+ assertThat(status.msg).isEqualTo("test_string")
+ }
+
+ @Test
+ fun onFingerprintHelp_helpAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricHelp(
+ 1,
+ "test_string",
+ BiometricSourceType.FINGERPRINT,
+ )
+
+ val status = authenticationStatus as HelpFingerprintAuthenticationStatus
+ assertThat(status.msgId).isEqualTo(1)
+ assertThat(status.msg).isEqualTo("test_string")
+ }
+
+ @Test
+ fun onFingerprintAcquired_acquiredAuthenticationStatus() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAcquired(
+ BiometricSourceType.FINGERPRINT,
+ 5,
+ )
+
+ val status = authenticationStatus as AcquiredFingerprintAuthenticationStatus
+ assertThat(status.acquiredInfo).isEqualTo(5)
+ }
+
+ @Test
+ fun onFaceCallbacks_fingerprintAuthenticationStatusIsUnchanged() =
+ testScope.runTest {
+ val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+ runCurrent()
+
+ verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+ updateMonitorCallback.value.onBiometricAuthenticated(
+ 0,
+ BiometricSourceType.FACE,
+ true,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricAuthFailed(
+ BiometricSourceType.FACE,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricHelp(
+ 1,
+ "test_string",
+ BiometricSourceType.FACE,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricAcquired(
+ BiometricSourceType.FACE,
+ 5,
+ )
+ assertThat(authenticationStatus).isNull()
+
+ updateMonitorCallback.value.onBiometricError(
+ 1,
+ "test_string",
+ BiometricSourceType.FACE,
+ )
+ assertThat(authenticationStatus).isNull()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index f541815d2711..ba7d3490e56d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -31,11 +31,14 @@ import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -71,6 +74,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var screenLifecycle: ScreenLifecycle
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
@@ -92,6 +96,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
KeyguardRepositoryImpl(
statusBarStateController,
wakefulnessLifecycle,
+ screenLifecycle,
biometricUnlockController,
keyguardStateController,
keyguardBypassController,
@@ -160,6 +165,16 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun dozeTimeTick() =
+ testScope.runTest {
+ var dozeTimeTickValue = collectLastValue(underTest.dozeTimeTick)
+ underTest.dozeTimeTick()
+ runCurrent()
+
+ assertThat(dozeTimeTickValue()).isNull()
+ }
+
+ @Test
fun isKeyguardShowing() =
testScope.runTest {
whenever(keyguardStateController.isShowing).thenReturn(false)
@@ -371,6 +386,48 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun screenModel() =
+ testScope.runTest {
+ val values = mutableListOf<ScreenModel>()
+ val job = underTest.screenModel.onEach(values::add).launchIn(this)
+
+ runCurrent()
+ val captor = argumentCaptor<ScreenLifecycle.Observer>()
+ verify(screenLifecycle).addObserver(captor.capture())
+
+ whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_TURNING_ON)
+ captor.value.onScreenTurningOn()
+ runCurrent()
+
+ whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON)
+ captor.value.onScreenTurnedOn()
+ runCurrent()
+
+ whenever(screenLifecycle.getScreenState())
+ .thenReturn(ScreenLifecycle.SCREEN_TURNING_OFF)
+ captor.value.onScreenTurningOff()
+ runCurrent()
+
+ whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_OFF)
+ captor.value.onScreenTurnedOff()
+ runCurrent()
+
+ assertThat(values.map { it.state })
+ .isEqualTo(
+ listOf(
+ // Initial value will be OFF
+ ScreenState.SCREEN_OFF,
+ ScreenState.SCREEN_TURNING_ON,
+ ScreenState.SCREEN_ON,
+ ScreenState.SCREEN_TURNING_OFF,
+ ScreenState.SCREEN_OFF,
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
fun isUdfpsSupported() =
testScope.runTest {
whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt
new file mode 100644
index 000000000000..3389fa9a48af
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import android.hardware.fingerprint.FingerprintManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED
+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.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import com.android.systemui.util.mockito.whenever
+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
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BiometricMessageInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: BiometricMessageInteractor
+ private lateinit var testScope: TestScope
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+
+ @Mock private lateinit var indicationHelper: IndicationHelper
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ underTest =
+ BiometricMessageInteractor(
+ mContext.resources,
+ fingerprintAuthRepository,
+ fingerprintPropertyRepository,
+ indicationHelper,
+ keyguardUpdateMonitor,
+ )
+ }
+
+ @Test
+ fun fingerprintErrorMessage() =
+ testScope.runTest {
+ val fingerprintErrorMessage by collectLastValue(underTest.fingerprintErrorMessage)
+
+ // GIVEN FINGERPRINT_ERROR_HW_UNAVAILABLE should NOT be suppressed
+ whenever(
+ indicationHelper.shouldSuppressErrorMsg(
+ FINGERPRINT,
+ FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+ )
+ )
+ .thenReturn(false)
+
+ // WHEN authentication status error is FINGERPRINT_ERROR_HW_UNAVAILABLE
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintErrorMessage is updated
+ assertThat(fingerprintErrorMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintErrorMessage?.type).isEqualTo(BiometricMessageType.ERROR)
+ assertThat(fingerprintErrorMessage?.id)
+ .isEqualTo(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE)
+ assertThat(fingerprintErrorMessage?.message).isEqualTo("test")
+ }
+
+ @Test
+ fun fingerprintErrorMessage_suppressedError() =
+ testScope.runTest {
+ val fingerprintErrorMessage by collectLastValue(underTest.fingerprintErrorMessage)
+
+ // GIVEN FINGERPRINT_ERROR_HW_UNAVAILABLE should be suppressed
+ whenever(
+ indicationHelper.shouldSuppressErrorMsg(
+ FINGERPRINT,
+ FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+ )
+ )
+ .thenReturn(true)
+
+ // WHEN authentication status error is FINGERPRINT_ERROR_HW_UNAVAILABLE
+ fingerprintAuthRepository.setAuthenticationStatus(
+ ErrorFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintErrorMessage isn't update - it's still null
+ assertThat(fingerprintErrorMessage).isNull()
+ }
+
+ @Test
+ fun fingerprintHelpMessage() =
+ testScope.runTest {
+ val fingerprintHelpMessage by collectLastValue(underTest.fingerprintHelpMessage)
+
+ // GIVEN primary auth is NOT required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+
+ // WHEN authentication status help is FINGERPRINT_ACQUIRED_IMAGER_DIRTY
+ fingerprintAuthRepository.setAuthenticationStatus(
+ HelpFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintHelpMessage is updated
+ assertThat(fingerprintHelpMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintHelpMessage?.type).isEqualTo(BiometricMessageType.HELP)
+ assertThat(fingerprintHelpMessage?.id)
+ .isEqualTo(FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY)
+ assertThat(fingerprintHelpMessage?.message).isEqualTo("test")
+ }
+
+ @Test
+ fun fingerprintHelpMessage_primaryAuthRequired() =
+ testScope.runTest {
+ val fingerprintHelpMessage by collectLastValue(underTest.fingerprintHelpMessage)
+
+ // GIVEN primary auth is required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false)
+
+ // WHEN authentication status help is FINGERPRINT_ACQUIRED_IMAGER_DIRTY
+ fingerprintAuthRepository.setAuthenticationStatus(
+ HelpFingerprintAuthenticationStatus(
+ msgId = FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+ msg = "test"
+ )
+ )
+
+ // THEN fingerprintHelpMessage isn't update - it's still null
+ assertThat(fingerprintHelpMessage).isNull()
+ }
+
+ @Test
+ fun fingerprintFailMessage_nonUdfps() =
+ testScope.runTest {
+ val fingerprintFailMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+ // GIVEN primary auth is NOT required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+
+ // GIVEN rear fingerprint (not UDFPS)
+ fingerprintPropertyRepository.setProperties(
+ 0,
+ SensorStrength.STRONG,
+ FingerprintSensorType.REAR,
+ mapOf()
+ )
+
+ // WHEN authentication status fail
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN fingerprintFailMessage is updated
+ assertThat(fingerprintFailMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintFailMessage?.type).isEqualTo(BiometricMessageType.FAIL)
+ assertThat(fingerprintFailMessage?.id)
+ .isEqualTo(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+ assertThat(fingerprintFailMessage?.message)
+ .isEqualTo(
+ mContext.resources.getString(
+ com.android.internal.R.string.fingerprint_error_not_match
+ )
+ )
+ }
+
+ @Test
+ fun fingerprintFailMessage_udfps() =
+ testScope.runTest {
+ val fingerprintFailMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+ // GIVEN primary auth is NOT required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+
+ // GIVEN UDFPS
+ fingerprintPropertyRepository.setProperties(
+ 0,
+ SensorStrength.STRONG,
+ FingerprintSensorType.UDFPS_OPTICAL,
+ mapOf()
+ )
+
+ // WHEN authentication status fail
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN fingerprintFailMessage is updated to udfps message
+ assertThat(fingerprintFailMessage?.source).isEqualTo(FINGERPRINT)
+ assertThat(fingerprintFailMessage?.type).isEqualTo(BiometricMessageType.FAIL)
+ assertThat(fingerprintFailMessage?.id)
+ .isEqualTo(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+ assertThat(fingerprintFailMessage?.message)
+ .isEqualTo(
+ mContext.resources.getString(
+ com.android.internal.R.string.fingerprint_udfps_error_not_match
+ )
+ )
+ }
+
+ @Test
+ fun fingerprintFailedMessage_primaryAuthRequired() =
+ testScope.runTest {
+ val fingerprintFailedMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+ // GIVEN primary auth is required
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false)
+
+ // WHEN authentication status fail
+ fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+ // THEN fingerprintFailedMessage isn't update - it's still null
+ assertThat(fingerprintFailedMessage).isNull()
+ }
+}
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 3e81cd336824..ced0a213ca97 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
@@ -38,10 +38,9 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -121,8 +120,8 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
mock(KeyguardStateController::class.java),
bouncerRepository,
mock(BiometricSettingsRepository::class.java),
- FakeDeviceEntryFingerprintAuthRepository(),
FakeSystemClock(),
+ keyguardUpdateMonitor,
),
keyguardTransitionInteractor,
featureFlags,
@@ -160,7 +159,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
underTest.onDeviceLifted()
- val outputValue = authenticationStatus()!! as ErrorAuthenticationStatus
+ val outputValue = authenticationStatus()!! as ErrorFaceAuthenticationStatus
assertThat(outputValue.msgId)
.isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT)
assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
new file mode 100644
index 000000000000..272d686e974b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.core
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogMessageImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import org.mockito.Mockito.anyString
+
+/**
+ * A fake [LogBuffer] used for testing that obtains a real [LogMessage] to prevent a
+ * [NullPointerException].
+ */
+class FakeLogBuffer private constructor() {
+ class Factory private constructor() {
+ companion object {
+ fun create(): LogBuffer {
+ val logBuffer = mock<LogBuffer>()
+ whenever(
+ logBuffer.obtain(
+ tag = anyString(),
+ level = any(),
+ messagePrinter = any(),
+ exception = nullable(),
+ )
+ )
+ .thenReturn(LogMessageImpl.Factory.create())
+ return logBuffer
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 608778e05dad..1dc8453a90ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -87,6 +88,7 @@ import java.util.function.Consumer;
@RunWithLooper
public class ExpandableNotificationRowTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationTestHelper mNotificationTestHelper;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@@ -96,12 +98,10 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
-
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false);
- mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
}
@Test
@@ -183,6 +183,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ public void testSetSensitiveOnNotifRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnNotifRowNotifiesOfHeightChange();
+ }
+
+ @Test
public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception {
// GIVEN a sensitive notification row that's currently redacted
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
@@ -199,10 +207,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
// WHEN the row is set to no longer be sensitive
row.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout());
assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(row), eq(false));
+ verify(listener).onHeightChanged(eq(row), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnGroupRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnGroupRowNotifiesOfHeightChange();
}
@Test
@@ -222,10 +239,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
// WHEN the row is set to no longer be sensitive
group.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout());
assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(group), eq(false));
+ verify(listener).onHeightChanged(eq(group), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange();
}
@Test
@@ -254,7 +280,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
assertThat(publicRow.getPrivateLayout().getMinHeight())
.isEqualTo(publicRow.getPublicLayout().getMinHeight());
- verify(listener, never()).onHeightChanged(eq(publicRow), eq(false));
+ verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean());
}
private void measureAndLayout(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1a644d3540b0..d21029d33d5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,12 +48,16 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +94,7 @@ import com.android.systemui.wmshell.BubblesTestActivity;
import org.mockito.ArgumentCaptor;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -130,14 +135,24 @@ public class NotificationTestHelper {
private final NotificationDismissibilityProvider mDismissibilityProvider;
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
- private FeatureFlags mFeatureFlags;
+ private final FakeFeatureFlags mFeatureFlags;
public NotificationTestHelper(
Context context,
TestableDependency dependency,
TestableLooper testLooper) {
+ this(context, dependency, testLooper, new FakeFeatureFlags());
+ }
+
+ public NotificationTestHelper(
+ Context context,
+ TestableDependency dependency,
+ TestableLooper testLooper,
+ @NonNull FakeFeatureFlags featureFlags) {
mContext = context;
mTestLooper = testLooper;
+ mFeatureFlags = Objects.requireNonNull(featureFlags);
+ dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogFactory.class);
@@ -183,17 +198,12 @@ public class NotificationTestHelper {
mFutureDismissalRunnable = mock(Runnable.class);
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
.thenReturn(mFutureDismissalRunnable);
- mFeatureFlags = mock(FeatureFlags.class);
}
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
mDefaultInflationFlags = defaultInflationFlags;
}
- public void setFeatureFlags(FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
- }
-
public ExpandableNotificationRowLogger getMockLogger() {
return mMockLogger;
}
@@ -527,6 +537,10 @@ public class NotificationTestHelper {
@InflationFlag int extraInflationFlags,
int importance)
throws Exception {
+ // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+ // set, but we do not want to override an existing value that is needed by a specific test.
+ mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
mRow = (ExpandableNotificationRow) inflater.inflate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 09382ec1945e..3d752880f423 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -20,7 +20,6 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -42,7 +41,6 @@ class AmbientStateTest : SysuiTestCase() {
private val bypassController = StackScrollAlgorithm.BypassController { false }
private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
- private val featureFlags = mock<FeatureFlags>()
private lateinit var sut: AmbientState
@@ -55,8 +53,7 @@ class AmbientStateTest : SysuiTestCase() {
sectionProvider,
bypassController,
statusBarKeyguardViewManager,
- largeScreenShadeInterpolator,
- featureFlags
+ largeScreenShadeInterpolator
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index f38881c5b521..4b145d8b0dd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,6 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -65,7 +64,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private FeatureFlags mFeatureFlag;
private NotificationSectionsManager mSectionsManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 8d751e3b2808..1dc0ab07349b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -9,7 +9,9 @@ import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerPr
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
@@ -20,6 +22,7 @@ import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,22 +37,32 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
-class NotificationShelfTest : SysuiTestCase() {
+open class NotificationShelfTest : SysuiTestCase() {
+
+ open val useShelfRefactor: Boolean = false
+ open val useSensitiveReveal: Boolean = false
+ private val flags = FakeFeatureFlags()
@Mock
private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
@Mock
- private lateinit var flags: FeatureFlags
- @Mock
private lateinit var ambientState: AmbientState
@Mock
private lateinit var hostLayoutController: NotificationStackScrollLayoutController
+ @Mock
+ private lateinit var hostLayout: NotificationStackScrollLayout
+ @Mock
+ private lateinit var roundnessManager: NotificationRoundnessManager
private lateinit var shelf: NotificationShelf
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mDependency.injectTestDependency(FeatureFlags::class.java, flags)
+ flags.set(Flags.NOTIFICATION_SHELF_REFACTOR, useShelfRefactor)
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal)
+ flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS)
val root = FrameLayout(context)
shelf = LayoutInflater.from(root.context)
.inflate(/* resource = */ R.layout.status_bar_notification_shelf,
@@ -57,10 +70,13 @@ class NotificationShelfTest : SysuiTestCase() {
/* attachToRoot = */false) as NotificationShelf
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
- whenever(ambientState.featureFlags).thenReturn(flags)
whenever(ambientState.isSmallScreen).thenReturn(true)
- shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
+ if (useShelfRefactor) {
+ shelf.bind(ambientState, hostLayout, roundnessManager)
+ } else {
+ shelf.bind(ambientState, hostLayoutController)
+ }
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -345,7 +361,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -372,7 +388,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -399,7 +415,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -426,7 +442,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -486,3 +502,25 @@ class NotificationShelfTest : SysuiTestCase() {
assertEquals(expectedAlpha, shelf.viewState.alpha)
}
}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithRefactorTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithSensitiveRevealTest : NotificationShelfTest() {
+ override val useSensitiveReveal: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithBothFlagsTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+ override val useSensitiveReveal: Boolean = true
+}
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 ee8325ec02b5..07eadf7c9bb4 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
@@ -54,7 +54,6 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -119,6 +118,7 @@ import java.util.Optional;
@RunWith(AndroidTestingRunner.class)
public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationsController mNotificationsController;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -157,7 +157,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@Mock private NotificationIconAreaController mIconAreaController;
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 8ad271bef2e4..72fcdec3c44c 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
@@ -68,7 +68,9 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -106,6 +108,7 @@ import java.util.ArrayList;
@TestableLooper.RunWithLooper
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
private AmbientState mAmbientState;
@@ -129,7 +132,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- @Mock private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@@ -143,11 +145,25 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mNotificationSectionsManager,
mBypassController,
mStatusBarKeyguardViewManager,
- mLargeScreenShadeInterpolator,
- mFeatureFlags
+ mLargeScreenShadeInterpolator
));
+ // Register the debug flags we use
+ assertFalse(Flags.NSSL_DEBUG_LINES.getDefault());
+ assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault());
+ mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false);
+ mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
+
+ // Register the feature flags we use
+ // TODO: Ideally we wouldn't need to set these unless a test actually reads them,
+ // and then we would test both configurations, but currently they are all read
+ // in the constructor.
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
+ mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
+
// Inject dependencies before initializing the layout
+ mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectTestDependency(
@@ -176,13 +192,18 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setShelfController(notificationShelfController);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelfController(notificationShelfController);
+ }
mStackScroller.setNotificationsController(mNotificationsController);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelf(mNotificationShelf);
+ }
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
@@ -899,7 +920,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testWindowInsetAnimationProgress_updatesBottomInset() {
int bottomImeInset = 100;
- mStackScrollerInternal.setAnimatedInsetsEnabled(true);
WindowInsets windowInsets = new WindowInsets.Builder()
.setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build();
ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index df65c09eb8a9..85a2bdd21073 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -83,7 +84,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
private Handler mHandler;
private ExpandableNotificationRow mNotificationRow;
private Runnable mFalsingCheck;
- private FeatureFlags mFeatureFlags;
+ private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
private static final int FAKE_ROW_WIDTH = 20;
private static final int FAKE_ROW_HEIGHT = 20;
@@ -96,7 +97,6 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
- mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper(
mContext.getResources(),
ViewConfiguration.get(mContext),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 45725ced521c..e30947ce84bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -18,6 +18,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationTargetsHelperTest : SysuiTestCase() {
+ private val featureFlags = FakeFeatureFlags()
lateinit var notificationTestHelper: NotificationTestHelper
private val sectionsManager: NotificationSectionsManager = mock()
private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -26,10 +27,10 @@ class NotificationTargetsHelperTest : SysuiTestCase() {
fun setUp() {
allowTestableLooperAsMainThread()
notificationTestHelper =
- NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags)
}
- private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags())
+ private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags)
@Test
fun targetsForFirstNotificationInGroup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 4c97d20c5da8..987861d3f133 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -8,7 +8,6 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
@@ -45,7 +44,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
private val dumpManager = mock<DumpManager>()
private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val notificationShelf = mock<NotificationShelf>()
- private val featureFlags = mock<FeatureFlags>()
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
@@ -56,7 +54,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
/* bypassController */ { false },
mStatusBarKeyguardViewManager,
largeScreenShadeInterpolator,
- featureFlags,
)
private val testableResources = mContext.getOrCreateTestableResources()
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 9c7f6190de44..33144f233a71 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
@@ -64,7 +64,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -117,6 +117,7 @@ import java.util.Optional;
public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private static final int DISPLAY_ID = 0;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
private AssistManager mAssistManager;
@@ -256,7 +257,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
mPowerInteractor,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mUserTracker
);
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 cd8aaa2685c2..9c52788dc2eb 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
@@ -79,7 +79,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
- private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private final NotificationsInteractor mNotificationsInteractor =
mock(NotificationsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
@@ -118,7 +117,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mCentralSurfaces,
mNotificationsInteractor,
mock(LockscreenShadeTransitionController.class),
mock(PowerInteractor.class),
@@ -202,7 +200,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
- when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 391c8ca4d286..7c285b8aa1a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,6 +102,8 @@ public class RemoteInputViewTest extends SysuiTestCase {
"com.android.sysuitest.dummynotificationsender";
private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+
@Mock private RemoteInputController mController;
@Mock private ShortcutManager mShortcutManager;
@Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
@@ -453,8 +455,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
private RemoteInputViewController bindController(
RemoteInputView view,
NotificationEntry entry) {
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
+ mFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
view,
entry,
@@ -462,7 +463,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
mController,
mShortcutManager,
mUiEventLoggerFake,
- fakeFeatureFlags
+ mFeatureFlags
);
viewController.bind();
return viewController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index d8e418a7815c..b13cb72dc944 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -26,6 +26,7 @@ import android.view.ViewRootImpl
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -56,6 +57,7 @@ class WallpaperControllerTest : SysuiTestCase() {
private lateinit var viewRootImpl: ViewRootImpl
@Mock
private lateinit var windowToken: IBinder
+ private val wallpaperRepository = FakeWallpaperRepository()
@JvmField
@Rule
@@ -69,7 +71,7 @@ class WallpaperControllerTest : SysuiTestCase() {
`when`(root.windowToken).thenReturn(windowToken)
`when`(root.isAttachedToWindow).thenReturn(true)
- wallaperController = WallpaperController(wallpaperManager)
+ wallaperController = WallpaperController(wallpaperManager, wallpaperRepository)
wallaperController.rootView = root
}
@@ -90,9 +92,9 @@ class WallpaperControllerTest : SysuiTestCase() {
@Test
fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
- wallaperController.onWallpaperInfoUpdated(createWallpaperInfo(
+ wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(
useDefaultTransition = false
- ))
+ )
wallaperController.setUnfoldTransitionZoom(0.5f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 6fc36b08250b..fe5024fdc0a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -16,9 +16,11 @@
package com.android.systemui.wallpapers.data.repository
+import android.app.WallpaperInfo
import kotlinx.coroutines.flow.MutableStateFlow
/** Fake implementation of the wallpaper repository. */
class FakeWallpaperRepository : WallpaperRepository {
+ override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index 132b9b4a02e1..f8b096a7579b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -65,6 +65,171 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun wallpaperInfo_nullInfo() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun wallpaperInfo_hasInfoFromManager() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ assertThat(latest).isEqualTo(UNSUPPORTED_WP)
+ }
+
+ @Test
+ fun wallpaperInfo_initialValueIsFetched() =
+ testScope.runTest {
+ whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
+ .thenReturn(SUPPORTED_WP)
+ userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP))
+ userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
+
+ // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
+ // value for the wallpaper
+ assertThat(underTest.wallpaperInfo.value).isEqualTo(SUPPORTED_WP)
+ }
+
+ @Test
+ fun wallpaperInfo_updatesOnUserChanged() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ val user3 = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+ val user3Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user3.id)).thenReturn(user3Wp)
+
+ val user4 = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+ val user4Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user4.id)).thenReturn(user4Wp)
+
+ userRepository.setUserInfos(listOf(user3, user4))
+
+ // WHEN user3 is selected
+ userRepository.setSelectedUserInfo(user3)
+
+ // THEN user3's wallpaper is used
+ assertThat(latest).isEqualTo(user3Wp)
+
+ // WHEN the user is switched to user4
+ userRepository.setSelectedUserInfo(user4)
+
+ // THEN user4's wallpaper is used
+ assertThat(latest).isEqualTo(user4Wp)
+ }
+
+ @Test
+ fun wallpaperInfo_doesNotUpdateOnUserChanging() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ val user3 = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+ val user3Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user3.id)).thenReturn(user3Wp)
+
+ val user4 = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+ val user4Wp = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(user4.id)).thenReturn(user4Wp)
+
+ userRepository.setUserInfos(listOf(user3, user4))
+
+ // WHEN user3 is selected
+ userRepository.setSelectedUserInfo(user3)
+
+ // THEN user3's wallpaper is used
+ assertThat(latest).isEqualTo(user3Wp)
+
+ // WHEN the user has started switching to user4 but hasn't finished yet
+ userRepository.selectedUser.value =
+ SelectedUserModel(user4, SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // THEN the wallpaper still matches user3
+ assertThat(latest).isEqualTo(user3Wp)
+ }
+
+ @Test
+ fun wallpaperInfo_updatesOnIntent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wallpaperInfo)
+
+ val wp1 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+
+ assertThat(latest).isEqualTo(wp1)
+
+ // WHEN the info is new and a broadcast is sent
+ val wp2 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp2)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the flow updates
+ assertThat(latest).isEqualTo(wp2)
+ }
+
+ @Test
+ fun wallpaperInfo_wallpaperNotSupported_alwaysNull() =
+ testScope.runTest {
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(false)
+
+ val latest by collectLastValue(underTest.wallpaperInfo)
+ assertThat(latest).isNull()
+
+ // Even WHEN there *is* current wallpaper
+ val wp1 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the value is still null because wallpaper isn't supported
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun wallpaperInfo_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() =
+ testScope.runTest {
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
+ false
+ )
+
+ val latest by collectLastValue(underTest.wallpaperInfo)
+ assertThat(latest).isNull()
+
+ // Even WHEN there *is* current wallpaper
+ val wp1 = mock<WallpaperInfo>()
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_WALLPAPER_CHANGED),
+ )
+
+ // THEN the value is still null because wallpaper isn't supported
+ assertThat(latest).isNull()
+ }
+
+ @Test
fun wallpaperSupportsAmbientMode_nullInfo_false() =
testScope.runTest {
val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
@@ -190,14 +355,12 @@ class WallpaperRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
- val info: WallpaperInfo = mock()
- whenever(info.supportsAmbientMode()).thenReturn(false)
- whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(info)
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
assertThat(latest).isFalse()
// WHEN the info now supports ambient mode and a broadcast is sent
- whenever(info.supportsAmbientMode()).thenReturn(true)
+ whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
Intent(Intent.ACTION_WALLPAPER_CHANGED),
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 4839eeba2124..17bb73b94ac2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -92,7 +92,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -305,6 +305,7 @@ public class BubblesTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private UserHandle mUser0;
@@ -423,7 +424,7 @@ public class BubblesTest extends SysuiTestCase {
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mNotifPipelineFlags,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -432,7 +433,8 @@ public class BubblesTest extends SysuiTestCase {
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b94f816e1ca4..36fa7e65d8ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -62,6 +62,32 @@ class FakeFeatureFlags : FeatureFlags {
}
}
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
private fun notifyFlagChanged(flag: Flag<*>) {
flagListeners[flag.id]?.let { listeners ->
listeners.forEach { listener ->
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2715aaa82253..548169e6cccd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.data.repository
import com.android.keyguard.FaceAuthUiEvent
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -29,16 +29,16 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
override val isAuthenticated = MutableStateFlow(false)
override val canRunFaceAuth = MutableStateFlow(false)
- private val _authenticationStatus = MutableStateFlow<AuthenticationStatus?>(null)
- override val authenticationStatus: Flow<AuthenticationStatus> =
+ private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
+ override val authenticationStatus: Flow<FaceAuthenticationStatus> =
_authenticationStatus.filterNotNull()
- fun setAuthenticationStatus(status: AuthenticationStatus) {
+ fun setAuthenticationStatus(status: FaceAuthenticationStatus) {
_authenticationStatus.value = status
}
- private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
- override val detectionStatus: Flow<DetectionStatus>
+ private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
+ override val detectionStatus: Flow<FaceDetectionStatus>
get() = _detectionStatus.filterNotNull()
- fun setDetectionStatus(status: DetectionStatus) {
+ fun setDetectionStatus(status: FaceDetectionStatus) {
_detectionStatus.value = status
}
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 4bfd3d64c98e..38791caf5bfc 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,32 +17,38 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
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 {
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
-
- private val _isRunning = MutableStateFlow(false)
- override val isRunning: Flow<Boolean>
- get() = _isRunning
-
- private var fpSensorType = MutableStateFlow<BiometricType?>(null)
- override val availableFpSensorType: Flow<BiometricType?>
- get() = fpSensorType
-
fun setLockedOut(lockedOut: Boolean) {
_isLockedOut.value = lockedOut
}
+ private val _isRunning = MutableStateFlow(false)
+ override val isRunning: Flow<Boolean>
+ get() = _isRunning
fun setIsRunning(value: Boolean) {
_isRunning.value = value
}
+ private var fpSensorType = MutableStateFlow<BiometricType?>(null)
+ override val availableFpSensorType: Flow<BiometricType?>
+ get() = fpSensorType
fun setAvailableFpSensorType(value: BiometricType?) {
fpSensorType.value = value
}
+
+ private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null)
+ override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+ get() = _authenticationStatus.filterNotNull()
+ fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) {
+ _authenticationStatus.value = status
+ }
}
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 b9d098fe2851..8428566270de 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
@@ -22,11 +22,14 @@ import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -56,6 +59,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isDozing = MutableStateFlow(false)
override val isDozing: StateFlow<Boolean> = _isDozing
+ private val _dozeTimeTick = MutableSharedFlow<Unit>()
+ override val dozeTimeTick = _dozeTimeTick
+
private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
@@ -86,6 +92,9 @@ class FakeKeyguardRepository : KeyguardRepository {
)
override val wakefulness = _wakefulnessModel
+ private val _screenModel = MutableStateFlow(ScreenModel(ScreenState.SCREEN_OFF))
+ override val screenModel = _screenModel
+
private val _isUdfpsSupported = MutableStateFlow(false)
private val _isKeyguardGoingAway = MutableStateFlow(false)
@@ -147,6 +156,10 @@ class FakeKeyguardRepository : KeyguardRepository {
_isDozing.value = isDozing
}
+ override fun dozeTimeTick() {
+ _dozeTimeTick.tryEmit(Unit)
+ }
+
override fun setLastDozeTapToWakePosition(position: Point) {
_lastDozeTapToWakePosition.value = position
}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 7c3a75f256f9..5a15f17a0922 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -49,6 +49,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
private boolean mWipeExternalStorage;
private boolean mWipeEsims;
+ private boolean mShowWipeProgress = true;
@Override
public void onReceive(final Context context, final Intent intent) {
@@ -77,8 +78,12 @@ public class MasterClearReceiver extends BroadcastReceiver {
return;
}
- final boolean shutdown = intent.getBooleanExtra("shutdown", false);
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
+
+ // Factory reset dialog has its own UI for the reset process, so suppress ours if indicated.
+ mShowWipeProgress = intent.getBooleanExtra(Intent.EXTRA_SHOW_WIPE_PROGRESS, true);
+
+ final boolean shutdown = intent.getBooleanExtra("shutdown", false);
mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
@@ -190,15 +195,19 @@ public class MasterClearReceiver extends BroadcastReceiver {
public WipeDataTask(Context context, Thread chainedTask) {
mContext = context;
mChainedTask = chainedTask;
- mProgressDialog = new ProgressDialog(context, R.style.Theme_DeviceDefault_System);
+ mProgressDialog = mShowWipeProgress
+ ? new ProgressDialog(context, R.style.Theme_DeviceDefault_System)
+ : null;
}
@Override
protected void onPreExecute() {
- mProgressDialog.setIndeterminate(true);
- mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
- mProgressDialog.show();
+ if (mProgressDialog != null) {
+ mProgressDialog.setIndeterminate(true);
+ mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
+ mProgressDialog.show();
+ }
}
@Override
@@ -214,7 +223,9 @@ public class MasterClearReceiver extends BroadcastReceiver {
@Override
protected void onPostExecute(Void result) {
- mProgressDialog.dismiss();
+ if (mProgressDialog != null && mProgressDialog.isShowing()) {
+ mProgressDialog.dismiss();
+ }
mChainedTask.start();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8012c2653277..c9769b3f9932 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19915,7 +19915,8 @@ public class ActivityManagerService extends IActivityManager.Stub
return superImpl.apply(code, new AttributionSource(shellUid,
Process.INVALID_PID, "com.android.shell",
attributionSource.getAttributionTag(), attributionSource.getToken(),
- /*renouncedPermissions*/ null, attributionSource.getNext()),
+ /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+ attributionSource.getNext()),
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
skiProxyOperation);
} finally {
@@ -19968,7 +19969,8 @@ public class ActivityManagerService extends IActivityManager.Stub
return superImpl.apply(clientId, code, new AttributionSource(shellUid,
Process.INVALID_PID, "com.android.shell",
attributionSource.getAttributionTag(), attributionSource.getToken(),
- /*renouncedPermissions*/ null, attributionSource.getNext()),
+ /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+ attributionSource.getNext()),
startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
proxiedAttributionFlags, attributionChainId);
@@ -19994,7 +19996,8 @@ public class ActivityManagerService extends IActivityManager.Stub
superImpl.apply(clientId, code, new AttributionSource(shellUid,
Process.INVALID_PID, "com.android.shell",
attributionSource.getAttributionTag(), attributionSource.getToken(),
- /*renouncedPermissions*/ null, attributionSource.getNext()),
+ /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+ attributionSource.getNext()),
skipProxyOperation);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/appop/OWNERS b/services/core/java/com/android/server/appop/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/services/core/java/com/android/server/appop/OWNERS
+++ b/services/core/java/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ecb21d010897..b3ef86994159 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -118,7 +118,6 @@ import android.system.keystore2.Domain;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Slog;
@@ -836,9 +835,6 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override // binder interface
public void systemReady() {
- if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
- EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
- }
checkWritePermission();
mHasSecureLockScreen = mContext.getPackageManager()
@@ -1093,9 +1089,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private final void checkPasswordHavePermission() {
- if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
- EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
- }
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave");
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2db724f51918..8e1ad6529bc0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2491,16 +2491,6 @@ public class NotificationManagerService extends SystemService {
getContext().registerReceiver(mReviewNotificationPermissionsReceiver,
ReviewNotificationPermissionsReceiver.getFilter(),
Context.RECEIVER_NOT_EXPORTED);
-
- mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
- new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(@NonNull String op, @NonNull String packageName,
- int userId) {
- mHandler.post(
- () -> handleNotificationPermissionChange(packageName, userId));
- }
- });
}
/**
@@ -3560,16 +3550,20 @@ public class NotificationManagerService extends SystemService {
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
+ sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
.setType(MetricsEvent.TYPE_ACTION)
.setPackageName(pkg)
.setSubtype(enabled ? 1 : 0));
mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
+ // Now, cancel any outstanding notifications that are part of a just-disabled app
+ if (!enabled) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0,
+ UserHandle.getUserId(uid), REASON_PACKAGE_BANNED);
+ }
- // Outstanding notifications from this package will be cancelled, and the package will
- // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the
- // callback from AppOpsManager.
+ handleSavePolicyFile();
}
/**
@@ -5889,21 +5883,6 @@ public class NotificationManagerService extends SystemService {
}
};
- private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) {
- int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId);
- if (uid == INVALID_UID) {
- Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId));
- return;
- }
- boolean hasPermission = mPermissionHelper.hasPermission(uid);
- sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission);
- if (!hasPermission) {
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null,
- /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId,
- REASON_PACKAGE_BANNED);
- }
- }
-
protected void checkNotificationListenerAccess() {
if (!isCallerSystemOrPhone()) {
getContext().enforceCallingPermission(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 36a0b0c0d8e9..1f5bd3e0cc60 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -75,6 +75,7 @@ import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
@@ -108,30 +109,34 @@ public class ZenModeHelper {
static final int RULE_LIMIT_PER_PACKAGE = 100;
// pkg|userId => uid
- protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+ @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
- @VisibleForTesting protected final NotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
- @VisibleForTesting protected ZenModeConfig mDefaultConfig;
+ private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
- protected final RingerModeDelegate mRingerModeDelegate = new
+ private final RingerModeDelegate mRingerModeDelegate = new
RingerModeDelegate();
@VisibleForTesting protected final ZenModeConditions mConditions;
- Object mConfigsLock = new Object();
+ private final Object mConfigsArrayLock = new Object();
+ @GuardedBy("mConfigsArrayLock")
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
- private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
- @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
+ private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+ private final ZenModeEventLogger mZenModeEventLogger;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
private int mUser = UserHandle.USER_SYSTEM;
+
+ private final Object mConfigLock = new Object();
+ @GuardedBy("mConfigLock")
@VisibleForTesting protected ZenModeConfig mConfig;
@VisibleForTesting protected AudioManagerInternal mAudioManager;
protected PackageManager mPm;
@@ -159,7 +164,7 @@ public class ZenModeHelper {
mDefaultConfig = readDefaultConfig(mContext.getResources());
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
}
mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -186,7 +191,7 @@ public class ZenModeHelper {
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity,
int callingUid) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity,
callingUid);
@@ -206,7 +211,7 @@ public class ZenModeHelper {
}
public boolean shouldIntercept(NotificationRecord record) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
}
}
@@ -221,7 +226,7 @@ public class ZenModeHelper {
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
@@ -250,7 +255,7 @@ public class ZenModeHelper {
public void onUserRemoved(int user) {
if (user < UserHandle.USER_SYSTEM) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.remove(user);
}
}
@@ -268,7 +273,7 @@ public class ZenModeHelper {
mUser = user;
if (DEBUG) Log.d(TAG, reason + " u=" + user);
ZenModeConfig config = null;
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
if (mConfigs.get(user) != null) {
config = mConfigs.get(user).copy();
}
@@ -278,7 +283,7 @@ public class ZenModeHelper {
config = mDefaultConfig.copy();
config.user = user;
}
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
cleanUpZenRules();
@@ -314,7 +319,7 @@ public class ZenModeHelper {
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return rules;
for (ZenRule rule : mConfig.automaticRules.values()) {
if (canManageAutomaticZenRule(rule)) {
@@ -327,7 +332,7 @@ public class ZenModeHelper {
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return null;
rule = mConfig.automaticRules.get(id);
}
@@ -364,7 +369,7 @@ public class ZenModeHelper {
}
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) {
throw new AndroidRuntimeException("Could not create rule");
}
@@ -387,7 +392,7 @@ public class ZenModeHelper {
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
if (DEBUG) {
Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
@@ -419,7 +424,7 @@ public class ZenModeHelper {
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
ZenRule ruleToRemove = newConfig.automaticRules.get(id);
@@ -450,7 +455,7 @@ public class ZenModeHelper {
public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -467,7 +472,7 @@ public class ZenModeHelper {
public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -481,7 +486,7 @@ public class ZenModeHelper {
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -491,6 +496,7 @@ public class ZenModeHelper {
}
}
+ @GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
if (rules == null || rules.isEmpty()) return;
@@ -538,7 +544,7 @@ public class ZenModeHelper {
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
count++;
@@ -555,7 +561,7 @@ public class ZenModeHelper {
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (pkg.equals(rule.getPkg())) {
count++;
@@ -588,19 +594,23 @@ public class ZenModeHelper {
protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
updateDefaultAutomaticRuleNames();
- for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
- ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
- // if default rule wasn't user-modified nor enabled, use localized name
- // instead of previous system name
- if (currRule != null && !currRule.modified && !currRule.enabled
- && !defaultRule.name.equals(currRule.name)) {
- if (canManageAutomaticZenRule(currRule)) {
- if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.name + " to " + defaultRule.name);
- // update default rule (if locale changed, name of rule will change)
- currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
- "locale changed", callingUid, fromSystemOrSystemUi);
+ synchronized (mConfigLock) {
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't user-modified nor enabled, use localized name
+ // instead of previous system name
+ if (currRule != null && !currRule.modified && !currRule.enabled
+ && !defaultRule.name.equals(currRule.name)) {
+ if (canManageAutomaticZenRule(currRule)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Locale change - updating default zen rule name "
+ + "from " + currRule.name + " to " + defaultRule.name);
+ }
+ // update default rule (if locale changed, name of rule will change)
+ currRule.name = defaultRule.name;
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ "locale changed", callingUid, fromSystemOrSystemUi);
+ }
}
}
}
@@ -686,7 +696,7 @@ public class ZenModeHelper {
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
if (!Global.isValidZenMode(zenMode)) return;
if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
@@ -715,7 +725,7 @@ public class ZenModeHelper {
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig.manualRule != null) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
@@ -737,14 +747,14 @@ public class ZenModeHelper {
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix);
pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
- synchronized(mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
dump(pw, prefix, "mConfig", mConfig);
}
@@ -833,7 +843,7 @@ public class ZenModeHelper {
Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
}
if (DEBUG) Log.d(TAG, reason);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
}
@@ -841,7 +851,7 @@ public class ZenModeHelper {
public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
throws IOException {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -856,7 +866,9 @@ public class ZenModeHelper {
* @return user-specified default notification policy for priority only do not disturb
*/
public Policy getNotificationPolicy() {
- return getNotificationPolicy(mConfig);
+ synchronized (mConfigLock) {
+ return getNotificationPolicy(mConfig);
+ }
}
private static Policy getNotificationPolicy(ZenModeConfig config) {
@@ -867,8 +879,8 @@ public class ZenModeHelper {
* Sets the global notification policy used for priority only do not disturb
*/
public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
- if (policy == null || mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (policy == null || mConfig == null) return;
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
@@ -881,7 +893,7 @@ public class ZenModeHelper {
*/
private void cleanUpZenRules() {
long currentTime = System.currentTimeMillis();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
final ZenModeConfig newConfig = mConfig.copy();
if (newConfig.automaticRules != null) {
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -906,7 +918,7 @@ public class ZenModeHelper {
* @return a copy of the zen mode configuration
*/
public ZenModeConfig getConfig() {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mConfig.copy();
}
}
@@ -918,7 +930,8 @@ public class ZenModeHelper {
return mConsolidatedPolicy.copy();
}
- public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ @GuardedBy("mConfigLock")
+ private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
callingUid, fromSystemOrSystemUi);
@@ -926,11 +939,12 @@ public class ZenModeHelper {
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
}
}
+ @GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, String reason,
ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
boolean fromSystemOrSystemUi) {
@@ -942,7 +956,7 @@ public class ZenModeHelper {
}
if (config.user != mUser) {
// simply store away for background users
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -951,7 +965,7 @@ public class ZenModeHelper {
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -979,6 +993,7 @@ public class ZenModeHelper {
* Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
+ @GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
@@ -993,7 +1008,7 @@ public class ZenModeHelper {
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- evaluateZenMode(reason, setRingerMode);
+ evaluateZenModeLocked(reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
@@ -1025,7 +1040,8 @@ public class ZenModeHelper {
}
@VisibleForTesting
- protected void evaluateZenMode(String reason, boolean setRingerMode) {
+ @GuardedBy("mConfigLock")
+ protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
if (mConfig == null) return;
final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1056,8 +1072,8 @@ public class ZenModeHelper {
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -1094,8 +1110,8 @@ public class ZenModeHelper {
}
private void updateConsolidatedPolicy(String reason) {
- if (mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
if (mConfig.manualRule != null) {
applyCustomPolicy(policy, mConfig.manualRule);
@@ -1293,7 +1309,7 @@ public class ZenModeHelper {
* Generate pulled atoms about do not disturb configurations.
*/
public void pullRules(List<StatsEvent> events) {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int numConfigs = mConfigs.size();
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
@@ -1319,6 +1335,7 @@ public class ZenModeHelper {
}
}
+ @GuardedBy("mConfigsArrayLock")
private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
List<StatsEvent> events) {
// Make the ID safe.
@@ -1389,7 +1406,7 @@ public class ZenModeHelper {
if (mZenMode == Global.ZEN_MODE_OFF
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
+ && !areAllPriorityOnlyRingerSoundsMuted())) {
// in priority only with ringer not muted, save ringer mode changes
// in dnd off, save ringer mode changes
setPreviousRingerModeSetting(ringerModeNew);
@@ -1410,8 +1427,7 @@ public class ZenModeHelper {
&& (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| mZenMode == Global.ZEN_MODE_ALARMS
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
- mConfig)))) {
+ && areAllPriorityOnlyRingerSoundsMuted()))) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
@@ -1430,6 +1446,12 @@ public class ZenModeHelper {
return ringerModeExternalOut;
}
+ private boolean areAllPriorityOnlyRingerSoundsMuted() {
+ synchronized (mConfigLock) {
+ return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig);
+ }
+ }
+
@Override
public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeInternal, VolumePolicy policy) {
@@ -1633,7 +1655,7 @@ public class ZenModeHelper {
private void emitRules() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mRuleCountLogTime);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
int numZenRules = mConfig.automaticRules.size();
if (mNumZenRules != numZenRules
|| since > MINIMUM_LOG_PERIOD_MS) {
@@ -1651,7 +1673,7 @@ public class ZenModeHelper {
private void emitDndType() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mTypeLogTimeMs);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
int zenType = !dndOn ? DND_OFF
: (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/ArchiveManager.java
index 99479f088577..54352060cd38 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/ArchiveManager.java
@@ -77,9 +77,8 @@ final class ArchiveManager {
snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
"archiveApp");
verifyCaller(callerPackageName, callingPackageName);
-
PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
- verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource());
+ verifyInstaller(packageName, ps.getInstallSource());
List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
ps.getPackageName(),
@@ -125,7 +124,7 @@ final class ArchiveManager {
Path.of("/TODO"), null);
activityInfos.add(activityInfo);
}
- // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there
+
InstallSource installSource = ps.getInstallSource();
String installerPackageName = installSource.mUpdateOwnerPackageName != null
? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
@@ -159,19 +158,13 @@ final class ArchiveManager {
}
}
- private static void verifyInstallOwnership(String packageName, String callingPackageName,
- InstallSource installSource) {
- if (!TextUtils.equals(installSource.mInstallerPackageName,
- callingPackageName)) {
+ private static void verifyInstaller(String packageName, InstallSource installSource) {
+ // TODO(b/291060290) Verify installer supports unarchiving
+ if (installSource.mUpdateOwnerPackageName == null
+ && installSource.mInstallerPackageName == null) {
throw new SecurityException(
- TextUtils.formatSimple("Caller is not the installer of record for %s.",
+ TextUtils.formatSimple("No installer found to archive app %s.",
packageName));
}
- String updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
- if (updateOwnerPackageName != null
- && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) {
- throw new SecurityException(
- TextUtils.formatSimple("Caller is not the update owner for %s.", packageName));
- }
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 13fd2f261652..134b041cb242 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -110,6 +110,7 @@ import android.app.backup.IBackupManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderType;
@@ -119,7 +120,6 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
@@ -184,7 +184,9 @@ import com.android.server.pm.pkg.PackageState;
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;
@@ -207,6 +209,7 @@ import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -1077,8 +1080,6 @@ final class InstallPackageHelper {
final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
final boolean isRollback =
request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK;
- final boolean extractProfile =
- ((installFlags & PackageManager.INSTALL_DONT_EXTRACT_BASELINE_PROFILES) == 0);
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (request.isInstallMove()) {
// moving a complete application; perform an initial scan on the new install location
@@ -1114,9 +1115,7 @@ final class InstallPackageHelper {
@ParsingPackageUtils.ParseFlags final int parseFlags =
mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
| ParsingPackageUtils.PARSE_ENFORCE_CODE
- | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0)
- | (extractProfile
- ? ParsingPackageUtils.PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK : 0);
+ | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
@@ -3940,23 +3939,6 @@ final class InstallPackageHelper {
}
}
- // If this is a system app we hadn't seen before, and this is a first boot or OTA,
- // we need to unstop it if it doesn't have a launcher entry.
- if (mPm.mShouldStopSystemPackagesByDefault && scanResult.mRequest.mPkgSetting == null
- && ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0)
- && ((scanFlags & SCAN_AS_SYSTEM) != 0)) {
- final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
- launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launcherIntent.setPackage(parsedPackage.getPackageName());
- final List<ResolveInfo> launcherActivities =
- mPm.snapshotComputer().queryIntentActivitiesInternal(launcherIntent, null,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
- if (launcherActivities.isEmpty()) {
- scanResult.mPkgSetting.setStopped(false, 0);
- }
- }
-
if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
// Continue monitoring loading progress of active incremental packages
@@ -4329,6 +4311,8 @@ final class InstallPackageHelper {
// - It's an APEX or overlay package since stopped state does not affect them.
// - It is enumerated with a <initial-package-state> tag having the stopped attribute
// set to false
+ // - It doesn't have an enabled and exported launcher activity, which means the user
+ // wouldn't have a way to un-stop it
final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0;
if (mPm.mShouldStopSystemPackagesByDefault
&& scanSystemPartition
@@ -4337,8 +4321,9 @@ final class InstallPackageHelper {
&& !parsedPackage.isOverlayIsStatic()
) {
String packageName = parsedPackage.getPackageName();
- if (!mPm.mInitialNonStoppedSystemPackages.contains(packageName)
- && !"android".contentEquals(packageName)) {
+ if (!"android".contentEquals(packageName)
+ && !mPm.mInitialNonStoppedSystemPackages.contains(packageName)
+ && hasLauncherEntry(parsedPackage)) {
scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP;
}
}
@@ -4348,6 +4333,26 @@ final class InstallPackageHelper {
return new Pair<>(scanResult, shouldHideSystemApp);
}
+ private static boolean hasLauncherEntry(ParsedPackage parsedPackage) {
+ final HashSet<String> categories = new HashSet<>();
+ categories.add(Intent.CATEGORY_LAUNCHER);
+ final List<ParsedActivity> activities = parsedPackage.getActivities();
+ for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) {
+ final ParsedActivity activity = activities.get(indexActivity);
+ if (!activity.isEnabled() || !activity.isExported()) {
+ continue;
+ }
+ final List<ParsedIntentInfo> intents = activity.getIntents();
+ for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) {
+ final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter();
+ if (intentFilter != null && intentFilter.matchCategories(categories) == null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Returns if forced apk verification can be skipped for the whole package, including splits.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2fb1ed1bbc57..8d64bd9fb66a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3456,10 +3456,6 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
- case "--no-profile":
- sessionParams.installFlags |=
- PackageManager.INSTALL_DONT_EXTRACT_BASELINE_PROFILES;
- break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4348,7 +4344,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
- pw.println(" [--enable-rollback] [--no-profile]");
+ pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--force-non-staged] [--staged-ready-timeout TIMEOUT]");
pw.println(" [PATH [SPLIT...]|-]");
@@ -4381,7 +4377,6 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --apex: install an .apex file, not an .apk");
pw.println(" --force-non-staged: force the installation to run under a non-staged");
pw.println(" session, which may complete without requiring a reboot");
- pw.println(" --no-profile: don't extract the profiles from the apk");
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
+ DEFAULT_STAGED_READY_TIMEOUT_MS);
pw.println(" milliseconds for pre-reboot verification to complete when");
@@ -4403,7 +4398,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
- pw.println(" [--multi-package] [--staged] [--no-profile] [--update-ownership]");
+ pw.println(" [--multi-package] [--staged] [--update-ownership]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
pw.println(" to push data into the session, and \"install-commit\" to finish.");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9b926973945c..494709c17e06 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1195,8 +1195,11 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
public PackageSetting setLoadingProgress(float progress) {
- mLoadingProgress = progress;
- onChanged();
+ // To prevent race conditions, we don't allow progress to ever go down
+ if (mLoadingProgress < progress) {
+ mLoadingProgress = progress;
+ onChanged();
+ }
return this;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7609073e149c..b01a89e672be 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1230,8 +1230,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (!fromDatasource && !checkPermission(context, permissionManagerServiceInt,
- permission, attributionSource.getUid(),
- attributionSource.getRenouncedPermissions())) {
+ permission, attributionSource)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -1292,12 +1291,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
case AppOpsManager.MODE_DEFAULT: {
if (!skipCurrentChecks && !checkPermission(context,
- permissionManagerServiceInt, permission, attributionSource.getUid(),
- attributionSource.getRenouncedPermissions())) {
+ permissionManagerServiceInt, permission, attributionSource)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (next != null && !checkPermission(context, permissionManagerServiceInt,
- permission, next.getUid(), next.getRenouncedPermissions())) {
+ permission, next)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
}
@@ -1326,8 +1324,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
- permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
- current.getRenouncedPermissions());
+ permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current);
while (true) {
final boolean skipCurrentChecks = (fromDatasource || next != null);
@@ -1342,12 +1339,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// If we already checked the permission for this one, skip the work
if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt,
- permission, current.getUid(), current.getRenouncedPermissions())) {
+ permission, current)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (next != null && !checkPermission(context, permissionManagerServiceInt,
- permission, next.getUid(), next.getRenouncedPermissions())) {
+ permission, next)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -1415,9 +1412,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private static boolean checkPermission(@NonNull Context context,
@NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
- @NonNull String permission, int uid, @NonNull Set<String> renouncedPermissions) {
- boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
- uid) == PackageManager.PERMISSION_GRANTED;
+ @NonNull String permission, AttributionSource attributionSource) {
+ int uid = attributionSource.getUid();
+ int deviceId = attributionSource.getDeviceId();
+ final Context deviceContext = context.getDeviceId() == deviceId ? context
+ : context.createDeviceContext(deviceId);
+ boolean permissionGranted = deviceContext.checkPermission(permission,
+ Process.INVALID_PID, uid) == PackageManager.PERMISSION_GRANTED;
// Override certain permissions checks for the shared isolated process for both
// HotwordDetectionService and VisualQueryDetectionService, which ordinarily cannot hold
@@ -1433,10 +1434,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
permissionGranted = hotwordServiceProvider != null
&& uid == hotwordServiceProvider.getUid();
}
-
+ Set<String> renouncedPermissions = attributionSource.getRenouncedPermissions();
if (permissionGranted && renouncedPermissions.contains(permission)
- && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
- /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
+ && deviceContext.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
+ Process.INVALID_PID, uid) == PackageManager.PERMISSION_GRANTED) {
return false;
}
return permissionGranted;
@@ -1507,8 +1508,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
// every attributionSource in the chain is registered with the system.
final boolean isChainStartTrusted = !hasChain || checkPermission(context,
- permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
- current.getRenouncedPermissions());
+ permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current);
while (true) {
final boolean skipCurrentChecks = (next != null);
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 d737b1c6bfa6..e2cb87e72c7a 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
@@ -50,7 +50,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
-import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.content.pm.parsing.PackageLite;
@@ -74,7 +73,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.ext.SdkExtensions;
-import android.os.incremental.IncrementalManager;
import android.permission.PermissionManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -242,11 +240,6 @@ public class ParsingPackageUtils {
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
public static final int PARSE_APK_IN_APEX = 1 << 9;
- /**
- * This flag is to determine whether to extract the baseline profiles from the apk or not.
- */
- public static final int PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK = 1 << 10;
-
public static final int PARSE_CHATTY = 1 << 31;
/** The total maximum number of activities, services, providers and activity-aliases */
@@ -258,16 +251,14 @@ public class ParsingPackageUtils {
private static final int MAX_PERMISSION_NAME_LENGTH = 512;
@IntDef(flag = true, prefix = { "PARSE_" }, value = {
- PARSE_APK_IN_APEX,
PARSE_CHATTY,
PARSE_COLLECT_CERTIFICATES,
PARSE_ENFORCE_CODE,
PARSE_EXTERNAL_STORAGE,
- PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK,
- PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
PARSE_IGNORE_PROCESSES,
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
+ PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ParseFlags {}
@@ -568,26 +559,6 @@ public class ParsingPackageUtils {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
- // 1. The apkFile is an apk file
- // 2. The flags include PARSE_EXTRACT_PROFILE_FROM_APK
- // 3. The apk patch is NOT an incremental path
- // 4. If the .dm file exists in the current apk directory, it means the caller
- // prepares the .dm file. Don't extract the profiles from the apk again.
- if (ApkLiteParseUtils.isApkFile(apkFile)
- && (flags & PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK) != 0
- && !IncrementalManager.isIncrementalPath(apkPath)
- && DexMetadataHelper.findDexMetadataForFile(apkFile) == null) {
- // Extract the baseline profiles from the apk if the profiles exist in the assets
- // directory in the apk.
- boolean extractedResult =
- DexMetadataHelper.extractBaselineProfilesToDexMetadataFileFromApk(assets,
- apkPath);
-
- if (DEBUG_JAR) {
- Slog.d(TAG, "Extract profiles " + (extractedResult ? "success" : "fail"));
- }
- }
-
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6e441bfb534e..7bbe8781e434 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6545,6 +6545,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
+ private static final String DP_EXIST = "DP=1";
private static final String NAME = "hdmi";
private boolean init(ExtconInfo hdmi) {
@@ -6575,7 +6576,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public Boolean parseState(ExtconInfo extconIfno, String state) {
// extcon event state changes from kernel4.9
// new state will be like STATE=HDMI=1
- return state.contains(HDMI_EXIST);
+ // or like STATE=DP=1 for newer kernel
+ return state.contains(HDMI_EXIST) || state.contains(DP_EXIST);
}
}
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 5ae697315ed1..95296210be80 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -184,13 +184,7 @@ public class FileIntegrityService extends SystemService {
}
private void loadAllCertificates() {
- // A better alternative to load certificates would be to read from .fs-verity kernel
- // keyring, which fsverity_init loads to during earlier boot time from the same sources
- // below. But since the read operation from keyring is not provided in kernel, we need to
- // duplicate the same loading logic here.
-
// Load certificates trusted by the device manufacturer.
- // NB: Directories need to be synced with system/security/fsverity_init/fsverity_init.cpp.
final String relativeDir = "etc/security/fsverity";
loadCertificatesFromDirectory(Environment.getRootDirectory().toPath()
.resolve(relativeDir));
diff --git a/services/core/java/com/android/server/vibrator/TEST_MAPPING b/services/core/java/com/android/server/vibrator/TEST_MAPPING
new file mode 100644
index 000000000000..f0a7e470f8fd
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksVibratorServicesTests",
+ "options": [
+ {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksVibratorServicesTests",
+ "options": [
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index c5f63ced989c..a6d5c19395b0 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -285,9 +285,9 @@ class ActivityMetricsLogger {
final LaunchingState mLaunchingState;
/** The type can be cold (new process), warm (new activity), or hot (bring to front). */
- final int mTransitionType;
+ int mTransitionType;
/** Whether the process was already running when the transition started. */
- final boolean mProcessRunning;
+ boolean mProcessRunning;
/** whether the process of the launching activity didn't have any active activity. */
final boolean mProcessSwitch;
/** The process state of the launching activity prior to the launch */
@@ -972,6 +972,19 @@ class ActivityMetricsLogger {
// App isn't attached to record yet, so match with info.
if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) {
info.mBindApplicationDelayMs = info.calculateCurrentDelay();
+ if (info.mProcessRunning) {
+ // It was HOT/WARM launch, but the process was died somehow right after the
+ // launch request.
+ info.mProcessRunning = false;
+ info.mTransitionType = TYPE_TRANSITION_COLD_LAUNCH;
+ final String msg = "Process " + info.mLastLaunchedActivity.info.processName
+ + " restarted";
+ Slog.i(TAG, msg);
+ if (info.mLaunchingState.mTraceName != null) {
+ Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg + "#"
+ + LaunchingState.sTraceSeqId);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e30673cb1f45..ea06b4295850 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7991,6 +7991,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLastReportedConfiguration.getMergedConfiguration())) {
ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */,
false /* ignoreVisibility */, true /* isRequestedOrientationChanged */);
+ if (mTransitionController.inPlayingTransition(this)) {
+ mTransitionController.mValidateActivityCompat.add(this);
+ }
}
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
@@ -9410,6 +9413,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (info.applicationInfo == null) {
return info.getMinAspectRatio();
}
+ if (mLetterboxUiController.shouldApplyUserMinAspectRatioOverride()) {
+ return mLetterboxUiController.getUserMinAspectRatio();
+ }
if (!mLetterboxUiController.shouldOverrideMinAspectRatio()) {
return info.getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e261916dffed..349d11568e71 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6404,9 +6404,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Don't do recursive work.
return;
}
- mInEnsureActivitiesVisible = true;
mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
try {
+ mInEnsureActivitiesVisible = true;
forAllRootTasks(rootTask -> {
rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7a201a77c966..e945bc1babd9 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -294,16 +294,15 @@ final class LetterboxConfiguration {
@NonNull private final SynchedDeviceConfig mDeviceConfig;
LetterboxConfiguration(@NonNull final Context systemUiContext) {
- this(systemUiContext,
- new LetterboxConfigurationPersister(systemUiContext,
- () -> readLetterboxHorizontalReachabilityPositionFromConfig(
- systemUiContext, /* forBookMode */ false),
- () -> readLetterboxVerticalReachabilityPositionFromConfig(
- systemUiContext, /* forTabletopMode */ false),
- () -> readLetterboxHorizontalReachabilityPositionFromConfig(
- systemUiContext, /* forBookMode */ true),
- () -> readLetterboxVerticalReachabilityPositionFromConfig(
- systemUiContext, /* forTabletopMode */ true)));
+ this(systemUiContext, new LetterboxConfigurationPersister(
+ () -> readLetterboxHorizontalReachabilityPositionFromConfig(
+ systemUiContext, /* forBookMode */ false),
+ () -> readLetterboxVerticalReachabilityPositionFromConfig(
+ systemUiContext, /* forTabletopMode */ false),
+ () -> readLetterboxHorizontalReachabilityPositionFromConfig(
+ systemUiContext, /* forBookMode */ true),
+ () -> readLetterboxVerticalReachabilityPositionFromConfig(
+ systemUiContext, /* forTabletopMode */ true)));
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
index 756339701590..38aa903e3954 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
@@ -23,7 +23,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.os.Environment;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
@@ -53,10 +52,8 @@ class LetterboxConfigurationPersister {
private static final String TAG =
TAG_WITH_CLASS_NAME ? "LetterboxConfigurationPersister" : TAG_WM;
- @VisibleForTesting
- static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
+ private static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
- private final Context mContext;
private final Supplier<Integer> mDefaultHorizontalReachabilitySupplier;
private final Supplier<Integer> mDefaultVerticalReachabilitySupplier;
private final Supplier<Integer> mDefaultBookModeReachabilitySupplier;
@@ -97,36 +94,32 @@ class LetterboxConfigurationPersister {
@NonNull
private final PersisterQueue mPersisterQueue;
- LetterboxConfigurationPersister(Context systemUiContext,
- Supplier<Integer> defaultHorizontalReachabilitySupplier,
- Supplier<Integer> defaultVerticalReachabilitySupplier,
- Supplier<Integer> defaultBookModeReachabilitySupplier,
- Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
- this(systemUiContext, defaultHorizontalReachabilitySupplier,
- defaultVerticalReachabilitySupplier,
- defaultBookModeReachabilitySupplier,
- defaultTabletopModeReachabilitySupplier,
+ LetterboxConfigurationPersister(
+ @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
+ this(defaultHorizontalReachabilitySupplier, defaultVerticalReachabilitySupplier,
+ defaultBookModeReachabilitySupplier, defaultTabletopModeReachabilitySupplier,
Environment.getDataSystemDirectory(), new PersisterQueue(),
- /* completionCallback */ null);
+ /* completionCallback */ null, LETTERBOX_CONFIGURATION_FILENAME);
}
@VisibleForTesting
- LetterboxConfigurationPersister(Context systemUiContext,
- Supplier<Integer> defaultHorizontalReachabilitySupplier,
- Supplier<Integer> defaultVerticalReachabilitySupplier,
- Supplier<Integer> defaultBookModeReachabilitySupplier,
- Supplier<Integer> defaultTabletopModeReachabilitySupplier,
- File configFolder,
- PersisterQueue persisterQueue, @Nullable Consumer<String> completionCallback) {
- mContext = systemUiContext.createDeviceProtectedStorageContext();
+ LetterboxConfigurationPersister(
+ @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
+ @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier,
+ @NonNull File configFolder, @NonNull PersisterQueue persisterQueue,
+ @Nullable Consumer<String> completionCallback,
+ @NonNull String letterboxConfigurationFileName) {
mDefaultHorizontalReachabilitySupplier = defaultHorizontalReachabilitySupplier;
mDefaultVerticalReachabilitySupplier = defaultVerticalReachabilitySupplier;
- mDefaultBookModeReachabilitySupplier =
- defaultBookModeReachabilitySupplier;
- mDefaultTabletopModeReachabilitySupplier =
- defaultTabletopModeReachabilitySupplier;
+ mDefaultBookModeReachabilitySupplier = defaultBookModeReachabilitySupplier;
+ mDefaultTabletopModeReachabilitySupplier = defaultTabletopModeReachabilitySupplier;
mCompletionCallback = completionCallback;
- final File prefFiles = new File(configFolder, LETTERBOX_CONFIGURATION_FILENAME);
+ final File prefFiles = new File(configFolder, letterboxConfigurationFileName);
mConfigurationFile = new AtomicFile(prefFiles);
mPersisterQueue = persisterQueue;
runWithDiskReadsThreadPolicy(this::readCurrentConfiguration);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 39f75703d71f..394105a646f1 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -40,6 +40,12 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.isFixedOrientation;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.screenOrientationToString;
+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_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -103,6 +109,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -237,6 +244,10 @@ final class LetterboxUiController {
// Counter for ActivityRecord#setRequestedOrientation
private int mSetOrientationRequestCounter = 0;
+ // The min aspect ratio override set by user
+ @PackageManager.UserMinAspectRatio
+ private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
+
// The CompatDisplayInsets of the opaque activity beneath the translucent one.
private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
@@ -1059,7 +1070,7 @@ final class LetterboxUiController {
private float getDefaultMinAspectRatioForUnresizableApps() {
if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()
- || mActivityRecord.getDisplayContent() == null) {
+ || mActivityRecord.getDisplayArea() == null) {
return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
> MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
@@ -1071,8 +1082,8 @@ final class LetterboxUiController {
float getSplitScreenAspectRatio() {
// Getting the same aspect ratio that apps get in split screen.
- final DisplayContent displayContent = mActivityRecord.getDisplayContent();
- if (displayContent == null) {
+ final DisplayArea displayArea = mActivityRecord.getDisplayArea();
+ if (displayArea == null) {
return getDefaultMinAspectRatioForUnresizableApps();
}
int dividerWindowWidth =
@@ -1080,7 +1091,7 @@ final class LetterboxUiController {
int dividerInsets =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
int dividerSize = dividerWindowWidth - dividerInsets * 2;
- final Rect bounds = new Rect(displayContent.getWindowConfiguration().getAppBounds());
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
if (bounds.width() >= bounds.height()) {
bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
bounds.right = bounds.centerX();
@@ -1091,14 +1102,57 @@ final class LetterboxUiController {
return computeAspectRatio(bounds);
}
+ boolean shouldApplyUserMinAspectRatioOverride() {
+ if (!mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()) {
+ return false;
+ }
+
+ try {
+ final int userAspectRatio = mActivityRecord.mAtmService.getPackageManager()
+ .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId);
+ mUserAspectRatio = userAspectRatio;
+ return userAspectRatio != USER_MIN_ASPECT_RATIO_UNSET;
+ } catch (RemoteException e) {
+ // Don't apply user aspect ratio override
+ Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e);
+ return false;
+ }
+ }
+
+ float getUserMinAspectRatio() {
+ switch (mUserAspectRatio) {
+ case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
+ return getDisplaySizeMinAspectRatio();
+ case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
+ return getSplitScreenAspectRatio();
+ case USER_MIN_ASPECT_RATIO_16_9:
+ return 16 / 9f;
+ case USER_MIN_ASPECT_RATIO_4_3:
+ return 4 / 3f;
+ case USER_MIN_ASPECT_RATIO_3_2:
+ return 3 / 2f;
+ default:
+ throw new AssertionError("Unexpected user min aspect ratio override: "
+ + mUserAspectRatio);
+ }
+ }
+
+ private float getDisplaySizeMinAspectRatio() {
+ final DisplayArea displayArea = mActivityRecord.getDisplayArea();
+ if (displayArea == null) {
+ return mActivityRecord.info.getMinAspectRatio();
+ }
+ final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+ return computeAspectRatio(bounds);
+ }
+
private float getDefaultMinAspectRatio() {
- final DisplayContent displayContent = mActivityRecord.getDisplayContent();
- if (displayContent == null
+ if (mActivityRecord.getDisplayArea() == null
|| !mLetterboxConfiguration
.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
}
- return computeAspectRatio(new Rect(displayContent.getBounds()));
+ return getDisplaySizeMinAspectRatio();
}
Resources getResources() {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index b71d918b987f..f33ecaa90531 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -684,6 +684,26 @@ class RecentTasks {
}
}
+ /**
+ * Removes the oldest recent task that is compatible with the given one. This is possible if
+ * the task windowing mode changed after being added to the Recents.
+ */
+ void removeCompatibleRecentTask(Task task) {
+ final int taskIndex = mTasks.indexOf(task);
+ if (taskIndex < 0) {
+ return;
+ }
+
+ final int candidateIndex = findRemoveIndexForTask(task, false /* includingSelf */);
+ if (candidateIndex == -1) {
+ // Nothing to trim
+ return;
+ }
+
+ final Task taskToRemove = taskIndex > candidateIndex ? task : mTasks.get(candidateIndex);
+ remove(taskToRemove);
+ }
+
void removeTasksByPackageName(String packageName, int userId) {
for (int i = mTasks.size() - 1; i >= 0; --i) {
final Task task = mTasks.get(i);
@@ -1546,6 +1566,10 @@ class RecentTasks {
* list (if any).
*/
private int findRemoveIndexForAddTask(Task task) {
+ return findRemoveIndexForTask(task, true /* includingSelf */);
+ }
+
+ private int findRemoveIndexForTask(Task task, boolean includingSelf) {
final int recentsCount = mTasks.size();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
@@ -1601,6 +1625,8 @@ class RecentTasks {
// existing task
continue;
}
+ } else if (!includingSelf) {
+ continue;
}
return i;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6e46f3f7e6f3..2f0c303ec839 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1849,9 +1849,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Don't do recursive work.
return;
}
-
+ mTaskSupervisor.beginActivityVisibilityUpdate();
try {
- mTaskSupervisor.beginActivityVisibilityUpdate();
// First the front root tasks. In case any are not fullscreen and are in front of home.
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index c914fa10687f..fe3094e2edf0 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -293,26 +293,7 @@ public class SafeActivityOptions {
throw new SecurityException(msg);
}
// Check if the caller is allowed to launch on the specified display area.
- final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
- TaskDisplayArea taskDisplayArea = daToken != null
- ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
-
- // If we do not have a task display area token, check if the launch task display area
- // feature id is specified.
- if (taskDisplayArea == null) {
- final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
- if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
- final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
- ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
- final DisplayContent dc = supervisor.mRootWindowContainer
- .getDisplayContent(launchDisplayId);
- if (dc != null) {
- taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
- tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
- }
- }
- }
-
+ final TaskDisplayArea taskDisplayArea = getLaunchTaskDisplayArea(options, supervisor);
if (aInfo != null && taskDisplayArea != null
&& !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
taskDisplayArea, aInfo)) {
@@ -428,6 +409,32 @@ public class SafeActivityOptions {
}
}
+ @VisibleForTesting
+ TaskDisplayArea getLaunchTaskDisplayArea(ActivityOptions options,
+ ActivityTaskSupervisor supervisor) {
+ final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+ TaskDisplayArea taskDisplayArea = daToken != null
+ ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+ if (taskDisplayArea != null) {
+ return taskDisplayArea;
+ }
+
+ // If we do not have a task display area token, check if the launch task display area
+ // feature id is specified.
+ final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
+ if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+ final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
+ ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
+ final DisplayContent dc = supervisor.mRootWindowContainer
+ .getDisplayContent(launchDisplayId);
+ if (dc != null) {
+ taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
+ tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
+ }
+ }
+ return taskDisplayArea;
+ }
+
private boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) {
if (atmService.mActiveVoiceInteractionServiceComponent == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 0c1f33ccedbc..92c0987d5636 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1020,7 +1020,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final WindowContainer<?> parent = getParent();
final Task thisTask = asTask();
if (thisTask != null && parent.asTask() == null
- && mTransitionController.isTransientHide(thisTask)) {
+ && mTransitionController.isTransientVisible(thisTask)) {
// Keep transient-hide root tasks visible. Non-root tasks still follow standard rule.
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a14354041b91..eaea53d555e2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -407,6 +407,36 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
+ /** Returns {@code true} if the task should keep visible if this is a transient transition. */
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mTransientLaunches == null) return false;
+ int occludedCount = 0;
+ final int numTransient = mTransientLaunches.size();
+ for (int i = numTransient - 1; i >= 0; --i) {
+ final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask();
+ if (transientRoot == null) continue;
+ final WindowContainer<?> rootParent = transientRoot.getParent();
+ if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
+ final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
+ .mOpaqueActivityHelper.getOpaqueActivity(rootParent);
+ if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
+ occludedCount++;
+ }
+ }
+ if (occludedCount == numTransient) {
+ for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
+ if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
+ // Keep transient activity visible until transition finished, so it won't pause
+ // with transient-hide tasks that may delay resuming the next top.
+ return true;
+ }
+ }
+ // Let transient-hide activities pause before transition is finished.
+ return false;
+ }
+ return isInTransientHide(task);
+ }
+
boolean canApplyDim(@NonNull Task task) {
if (mTransientLaunches == null) return true;
final Dimmer dimmer = task.getDimmer();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 881eddc03243..dfaa17494855 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.WindowConfiguration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
@@ -141,6 +142,14 @@ class TransitionController {
final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>();
/**
+ * List of activity-level participants. ActivityRecord is not expected to change independently,
+ * however, recent compatibility logic can now cause this at arbitrary times determined by
+ * client code. If it happens during an animation, the surface can be left at the wrong spot.
+ * TODO(b/290237710) remove when compat logic is moved.
+ */
+ final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>();
+
+ /**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
@@ -468,15 +477,22 @@ class TransitionController {
if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
return true;
}
- for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
- if (mWaitingTransitions.get(i).isInTransientHide(task)) return true;
- }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
}
return false;
}
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientVisible(task)) return true;
+ }
+ return false;
+ }
+
boolean canApplyDim(@Nullable Task task) {
if (task == null) {
// Always allow non-activity window.
@@ -896,6 +912,14 @@ class TransitionController {
}
}
mValidateCommitVis.clear();
+ for (int i = 0; i < mValidateActivityCompat.size(); ++i) {
+ ActivityRecord ar = mValidateActivityCompat.get(i);
+ if (ar.getSurfaceControl() == null) continue;
+ final Point tmpPos = new Point();
+ ar.getRelativePosition(tmpPos);
+ ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y);
+ }
+ mValidateActivityCompat.clear();
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0eb452d29736..164c8b013c84 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1615,6 +1615,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final int count = tasksToReparent.size();
for (int i = 0; i < count; ++i) {
final Task task = tasksToReparent.get(i);
+ final int prevWindowingMode = task.getWindowingMode();
if (syncId >= 0) {
addToSyncSet(syncId, task);
}
@@ -1628,6 +1629,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
false /*moveParents*/, "processChildrenTaskReparentHierarchyOp");
}
+ // Trim the compatible Recent task (if any) after the Task is reparented and now has
+ // a different windowing mode, in order to prevent redundant Recent tasks after
+ // reparenting.
+ if (prevWindowingMode != task.getWindowingMode()) {
+ mService.mTaskSupervisor.mRecentTasks.removeCompatibleRecentTask(task);
+ }
}
if (transition != null) transition.collect(newParent);
diff --git a/services/permission/OWNERS b/services/permission/OWNERS
index 6c6c9fc10d3b..e464038e68d9 100644
--- a/services/permission/OWNERS
+++ b/services/permission/OWNERS
@@ -1,4 +1,5 @@
-ashfall@google.com
+#Bug component: 137825
+
joecastro@google.com
ntmyren@google.com
zhanghai@google.com
diff --git a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
+++ b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
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 72c5333e0a02..410ae35aa790 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -328,7 +328,7 @@ public class BroadcastQueueTest {
eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
- mConstants.TIMEOUT = 100;
+ mConstants.TIMEOUT = 200;
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
index 7b1654549841..a8b0a7b5633d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
@@ -40,6 +40,7 @@ import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -169,14 +170,14 @@ public class ArchiveManagerTest {
}
@Test
- public void archiveApp_callerNotInstallerOfRecord() {
+ public void archiveApp_noInstallerFound() {
InstallSource otherInstallSource =
InstallSource.create(
CALLER_PACKAGE,
CALLER_PACKAGE,
- /* installerPackageName= */ "different",
+ /* installerPackageName= */ null,
Binder.getCallingUid(),
- CALLER_PACKAGE,
+ /* updateOwnerPackageName= */ null,
/* installerAttributionTag= */ null,
/* packageSource= */ 0);
when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
@@ -187,29 +188,8 @@ public class ArchiveManagerTest {
mIntentSender)
);
assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the installer of record for %s.", PACKAGE));
- }
-
- @Test
- public void archiveApp_callerNotUpdateOwner() {
- InstallSource otherInstallSource =
- InstallSource.create(
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- Binder.getCallingUid(),
- /* updateOwnerPackageName= */ "different",
- /* installerAttributionTag= */ null,
- /* packageSource= */ 0);
- when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
-
- Exception e = assertThrows(
- SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
- );
- assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the update owner for %s.", PACKAGE));
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ PACKAGE));
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 3ac59e9f0efe..f20633342759 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -33,6 +33,8 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.Looper;
+import android.os.Process;
+import android.util.SparseArray;
import org.junit.After;
import org.junit.Before;
@@ -244,6 +246,54 @@ public class PackageMonitorCallbackHelperTest {
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
+ @Test
+ public void testRegisterPackageMonitorCallbackInAllowList_callbackShouldCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(0, new int[] {Binder.getCallingUid()});
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Binder.getCallingUid());
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+ null /* instantUserIds */, broadcastAllowList);
+
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+ }
+
+ @Test
+ public void testRegisterPackageMonitorCallbackNotInAllowList_callbackShouldNotCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(0, new int[] {12345});
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Binder.getCallingUid());
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+ null /* instantUserIds */, broadcastAllowList);
+
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
+ }
+
+ @Test
+ public void testRegisterPackageMonitorCallbackNotInAllowListSystemUid_callbackShouldCalled()
+ throws Exception {
+ IRemoteCallback callback = createMockPackageMonitorCallback();
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(0, new int[] {12345});
+
+ mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+ Process.SYSTEM_UID);
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+ FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+ null /* instantUserIds */, broadcastAllowList);
+
+ verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+ }
+
private IRemoteCallback createMockPackageMonitorCallback() {
return spy(new IRemoteCallback.Stub() {
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
deleted file mode 100644
index 0f2c27c83791..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.attention.AttentionManagerInternal;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
-import android.hardware.SensorManager;
-import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.BatteryManagerInternal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
-import android.os.test.TestLooper;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.test.mock.MockContentResolver;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.lights.LightsManager;
-import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.power.PowerManagerService.BatteryReceiver;
-import com.android.server.power.PowerManagerService.Injector;
-import com.android.server.power.PowerManagerService.NativeWrapper;
-import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
-import com.android.server.power.batterysaver.BatterySaverController;
-import com.android.server.power.batterysaver.BatterySaverPolicy;
-import com.android.server.power.batterysaver.BatterySaverStateMachine;
-import com.android.server.power.batterysaver.BatterySavingStats;
-import com.android.server.testutils.OffsettableClock;
-
-import java.util.concurrent.Executor;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link com.android.server.power.PowerManagerService}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:PowerManagerServiceMockingTest
- */
-public class PowerManagerServiceMockingTest {
- private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
- private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
-
- private static final float BRIGHTNESS_FACTOR = 0.7f;
- private static final boolean BATTERY_SAVER_ENABLED = true;
-
- @Mock private BatterySaverController mBatterySaverControllerMock;
- @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
- @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
- @Mock private LightsManager mLightsManagerMock;
- @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
- @Mock private BatteryManagerInternal mBatteryManagerInternalMock;
- @Mock private ActivityManagerInternal mActivityManagerInternalMock;
- @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
- @Mock private DreamManagerInternal mDreamManagerInternalMock;
- @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
- @Mock private Notifier mNotifierMock;
- @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
- @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
- @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
- @Mock private DeviceStateManager mDeviceStateManagerMock;
-
- @Mock
- private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
-
- private PowerManagerService mService;
- private PowerSaveState mPowerSaveState;
- private ContextWrapper mContextSpy;
- private BatteryReceiver mBatteryReceiver;
- private UserSwitchedReceiver mUserSwitchedReceiver;
- private Resources mResourcesSpy;
- private OffsettableClock mClock;
- private TestLooper mTestLooper;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- FakeSettingsProvider.clearSettingsProvider();
-
- mPowerSaveState = new PowerSaveState.Builder()
- .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
- .setBrightnessFactor(BRIGHTNESS_FACTOR)
- .build();
- when(mBatterySaverPolicyMock.getBatterySaverPolicy(
- eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
- .thenReturn(mPowerSaveState);
- when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
- when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
- when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
- .thenReturn(true);
- when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
- when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
-
- addLocalServiceMock(LightsManager.class, mLightsManagerMock);
- addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
- addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
- addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
- addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
- addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
-
- mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
- mResourcesSpy = spy(mContextSpy.getResources());
- when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
-
- MockContentResolver cr = new MockContentResolver(mContextSpy);
- cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContextSpy.getContentResolver()).thenReturn(cr);
-
- when(mContextSpy.getSystemService(DeviceStateManager.class))
- .thenReturn(mDeviceStateManagerMock);
-
- Settings.Global.putInt(mContextSpy.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
-
- mClock = new OffsettableClock.Stopped();
- mTestLooper = new TestLooper(mClock::now);
- }
-
- private PowerManagerService createService() {
- mService = new PowerManagerService(mContextSpy, new Injector() {
- @Override
- Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
- SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
- Executor executor) {
- return mNotifierMock;
- }
-
- @Override
- SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
- return super.createSuspendBlocker(service, name);
- }
-
- @Override
- BatterySaverPolicy createBatterySaverPolicy(
- Object lock, Context context, BatterySavingStats batterySavingStats) {
- return mBatterySaverPolicyMock;
- }
-
- @Override
- BatterySaverController createBatterySaverController(
- Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
- BatterySavingStats batterySavingStats) {
- return mBatterySaverControllerMock;
- }
-
- @Override
- BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
- BatterySaverController batterySaverController) {
- return mBatterySaverStateMachineMock;
- }
-
- @Override
- NativeWrapper createNativeWrapper() {
- return mNativeWrapperMock;
- }
-
- @Override
- WirelessChargerDetector createWirelessChargerDetector(
- SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
- return mWirelessChargerDetectorMock;
- }
-
- @Override
- AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
- return mAmbientDisplayConfigurationMock;
- }
-
- @Override
- InattentiveSleepWarningController createInattentiveSleepWarningController() {
- return mInattentiveSleepWarningControllerMock;
- }
-
- @Override
- public SystemPropertiesWrapper createSystemPropertiesWrapper() {
- return mSystemPropertiesMock;
- }
-
- @Override
- PowerManagerService.Clock createClock() {
- return new PowerManagerService.Clock() {
- @Override
- public long uptimeMillis() {
- return mClock.now();
- }
-
- @Override
- public long elapsedRealtime() {
- return mClock.now();
- }
- };
- }
-
- @Override
- Handler createHandler(Looper looper, Handler.Callback callback) {
- return new Handler(mTestLooper.getLooper(), callback);
- }
-
- @Override
- void invalidateIsInteractiveCaches() {
- // Avoids an SELinux failure.
- }
- });
- return mService;
- }
-
- @After
- public void tearDown() throws Exception {
- LocalServices.removeServiceForTest(LightsManager.class);
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.removeServiceForTest(BatteryManagerInternal.class);
- LocalServices.removeServiceForTest(ActivityManagerInternal.class);
- LocalServices.removeServiceForTest(AttentionManagerInternal.class);
- LocalServices.removeServiceForTest(DreamManagerInternal.class);
- FakeSettingsProvider.clearSettingsProvider();
- }
-
- /**
- * Creates a mock and registers it to {@link LocalServices}.
- */
- private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
- LocalServices.removeServiceForTest(clazz);
- LocalServices.addService(clazz, mock);
- }
-
- private void advanceTime(long timeMs) {
- mClock.fastForward(timeMs);
- mTestLooper.dispatchAll();
- }
-
- @Test
- public void testUserActivityOnDeviceStateChange() {
- createService();
- mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
- mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
-
- final DisplayInfo info = new DisplayInfo();
- info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
- when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
-
- final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
- ArgumentCaptor.forClass(DeviceStateCallback.class);
- verify(mDeviceStateManagerMock).registerCallback(any(),
- deviceStateCallbackCaptor.capture());
-
- // Advance the time 10001 and verify that the device thinks it has been idle
- // for just less than that.
- mService.onUserActivity();
- advanceTime(10001);
- assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
-
- // Send a display state change event and advance the clock 10.
- final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
- deviceStateCallback.onStateChanged(1);
- final long timeToAdvance = 10;
- advanceTime(timeToAdvance);
-
- // Ensure that the device has been idle for only 10 (doesn't include the idle time
- // before the display state event).
- assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
- assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
-
- // Send the same state and ensure that does not trigger an update.
- deviceStateCallback.onStateChanged(1);
- advanceTime(timeToAdvance);
- final long newTime = timeToAdvance * 2;
-
- assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
- assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
- }
-}
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 236f90c0c3e5..938401521624 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -12,9 +12,19 @@ android_test {
static_libs: [
"frameworks-base-testutils",
+ "platform-compat-test-rules",
"platform-test-annotations",
"services.core",
"servicestests-utils",
+ "testables",
+ ],
+
+ libs: [
+ "android.test.mock",
+ ],
+
+ defaults: [
+ "modules-utils-testable-device-config-defaults",
],
platform_apis: true,
diff --git a/services/tests/powerservicetests/AndroidManifest.xml b/services/tests/powerservicetests/AndroidManifest.xml
index 3ace9d512adc..26d9eec21fb1 100644
--- a/services/tests/powerservicetests/AndroidManifest.xml
+++ b/services/tests/powerservicetests/AndroidManifest.xml
@@ -17,10 +17,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.powerservicetests">
- <!--
- Insert permissions here. eg:
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- -->
+ <!-- Permissions -->
+ <uses-permission android:name="android.permission.DEVICE_POWER"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+ <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION"/>
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
+ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
+
<application android:debuggable="true"
android:testOnly="true">
<uses-library android:name="android.test.mock" android:required="true" />
diff --git a/services/tests/powerservicetests/OWNERS b/services/tests/powerservicetests/OWNERS
index e740577fae90..aff093057a29 100644
--- a/services/tests/powerservicetests/OWNERS
+++ b/services/tests/powerservicetests/OWNERS
@@ -2,4 +2,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
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
index d3c0e354838b..d3c0e354838b 100644
--- a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 2f039654ca7e..2f039654ca7e 100644
--- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index fe31b9cbe558..fe31b9cbe558 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index c80ff45e7768..9463b2d9824e 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -60,6 +60,8 @@ import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.res.Resources;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -160,6 +162,7 @@ public class PowerManagerServiceTest {
@Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
@Mock private PowerManagerService.PermissionCheckerWrapper mPermissionCheckerWrapperMock;
@Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper;
+ @Mock private DeviceStateManager mDeviceStateManagerMock;
@Rule public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -2710,4 +2713,48 @@ public class PowerManagerServiceTest {
verify(mNotifierMock, never()).onUserActivity(anyInt(), anyInt(), anyInt());
}
+ @Test
+ public void testUserActivityOnDeviceStateChange() {
+ when(mContextSpy.getSystemService(DeviceStateManager.class))
+ .thenReturn(mDeviceStateManagerMock);
+
+ createService();
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+ when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+ final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
+ ArgumentCaptor.forClass(DeviceStateCallback.class);
+ verify(mDeviceStateManagerMock).registerCallback(any(),
+ deviceStateCallbackCaptor.capture());
+
+ // Advance the time 10001 and verify that the device thinks it has been idle
+ // for just less than that.
+ mService.onUserActivity();
+ advanceTime(10001);
+ assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
+
+ // Send a display state change event and advance the clock 10.
+ final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
+ deviceStateCallback.onStateChanged(1);
+ final long timeToAdvance = 10;
+ advanceTime(timeToAdvance);
+
+ // Ensure that the device has been idle for only 10 (doesn't include the idle time
+ // before the display state event).
+ assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
+ assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
+
+ // Send the same state and ensure that does not trigger an update.
+ deviceStateCallback.onStateChanged(1);
+ advanceTime(timeToAdvance);
+ final long newTime = timeToAdvance * 2;
+
+ assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
+ assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java
index fe6cc28f03d3..fe6cc28f03d3 100644
--- a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
index 6041e916ffc0..6041e916ffc0 100644
--- a/services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index 7af4b3d87a3a..7af4b3d87a3a 100644
--- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 27e6ef199fe2..bbbab2129d22 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -66,7 +66,6 @@ import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -308,7 +307,6 @@ public class WindowMagnificationManagerTest {
MagnificationScaleProvider.MAX_SCALE);
}
- @Ignore("b/278816260: We could refer to b/182561174#comment4 for solution.")
@Test
public void logTrackingTypingFocus_processScroll_logDuration() {
WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
diff --git a/services/tests/servicestests/src/com/android/server/appop/OWNERS b/services/tests/servicestests/src/com/android/server/appop/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/services/tests/servicestests/src/com/android/server/appop/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
new file mode 100644
index 000000000000..0ffa891ce3e1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
new file mode 100644
index 000000000000..419508ca5e17
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
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 a109d5cddd21..f552ab2dab60 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -406,7 +406,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
UriGrantsManagerInternal mUgmInternal;
@Mock
AppOpsManager mAppOpsManager;
- private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener;
@Mock
private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
mNotificationAssistantAccessGrantedCallback;
@@ -606,12 +605,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
SEARCH_SELECTOR_PKG);
- doAnswer(invocation -> {
- mOnPermissionChangeListener = invocation.getArgument(2);
- return null;
- }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(),
- any());
-
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -3224,6 +3217,48 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testUpdateAppNotifyCreatorBlock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
+ verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorUnblock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
public void testUpdateChannelNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -12139,134 +12174,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
}
- @Test
- public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage()
- throws RemoteException {
- // Have preexisting posted notifications from revoked package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from revoked package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other");
- assertThat(mService.mEnqueuedNotifications).hasSize(1);
- assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo(
- "other");
- }
-
- @Test
- public void onOpChanged_permissionStillGranted_notificationsAreNotAffected()
- throws RemoteException {
- // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission
- // being now granted, AND having previously posted notifications from said package) should
- // never happen (if we trust the broadcasts are correct). So this test is for a what-if
- // scenario, to verify we still handle it reasonably.
-
- // Have preexisting posted notifications from specific package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from specific package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
- }
-
- @Test
- public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse();
- }
-
- @Test
- public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue();
- }
-
- @Test
- public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception {
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("package", 1001, 1, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(1);
- when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn(
- true);
-
- // Start with granted permission and simulate effect of revoking it.
- when(mPermissionHelper.hasPermission(1001)).thenReturn(true);
- doAnswer(invocation -> {
- when(mPermissionHelper.hasPermission(1001)).thenReturn(false);
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0);
- return null;
- }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true);
-
- mBinderService.setNotificationsEnabledForPackage("package", 1001, false);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(0);
-
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
- verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null));
- }
-
private static <T extends Parcelable> T parcelAndUnparcel(T source,
Parcelable.Creator<T> creator) {
Parcel parcel = Parcel.obtain();
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 dedb8f170ee0..3ee75de23fdb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -771,7 +771,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
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
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -798,7 +798,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -825,7 +825,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -2269,7 +2269,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml
index 2a15c15fce41..a14ea5598758 100644
--- a/services/tests/vibrator/AndroidManifest.xml
+++ b/services/tests/vibrator/AndroidManifest.xml
@@ -31,8 +31,7 @@
<!-- Required to set always-on vibrations -->
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" />
- <application android:debuggable="true"
- android:testOnly="true">
+ <application android:debuggable="true">
<uses-library android:name="android.test.mock" android:required="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/services/tests/vibrator/AndroidTest.xml b/services/tests/vibrator/AndroidTest.xml
deleted file mode 100644
index d5ee3afaedb6..000000000000
--- a/services/tests/vibrator/AndroidTest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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 Vibrator 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="install-arg" value="-t" />
- <option name="test-file-name" value="FrameworksVibratorServicesTests.apk" />
- </target_preparer>
-
- <option name="test-tag" value="FrameworksVibratorServicesTests" />
-
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.framework.services.tests.vibrator" />
- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <option name="hidden-api-checks" value="false" />
- </test>
-</configuration>
diff --git a/services/tests/vibrator/TEST_MAPPING b/services/tests/vibrator/TEST_MAPPING
index f0a7e470f8fd..22b72fa4ff9e 100644
--- a/services/tests/vibrator/TEST_MAPPING
+++ b/services/tests/vibrator/TEST_MAPPING
@@ -1,21 +1,7 @@
{
- "presubmit": [
+ "imports": [
{
- "name": "FrameworksVibratorServicesTests",
- "options": [
- {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"},
- {"exclude-annotation": "org.junit.Ignore"}
- ]
- }
- ],
- "postsubmit": [
- {
- "name": "FrameworksVibratorServicesTests",
- "options": [
- {"exclude-annotation": "org.junit.Ignore"}
- ]
+ "path": "frameworks/base/services/core/java/com/android/server/vibrator"
}
]
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 5c3102d870d0..65e77dcd4ca9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -182,12 +182,12 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testLaunchState() {
- final ToIntFunction<Boolean> launchTemplate = doRelaunch -> {
+ final ToIntFunction<Runnable> launchTemplate = action -> {
clearInvocations(mLaunchObserver);
onActivityLaunched(mTopActivity);
notifyTransitionStarting(mTopActivity);
- if (doRelaunch) {
- mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity);
+ if (action != null) {
+ action.run();
}
final ActivityMetricsLogger.TransitionInfoSnapshot info =
notifyWindowsDrawn(mTopActivity);
@@ -199,21 +199,27 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// Assume that the process is started (ActivityBuilder has mocked the returned value of
// ATMS#getProcessController) but the activity has not attached process.
mTopActivity.app = null;
- assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(null))
.isEqualTo(WaitResult.LAUNCH_STATE_WARM);
mTopActivity.app = app;
mNewActivityCreated = false;
- assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(null))
.isEqualTo(WaitResult.LAUNCH_STATE_HOT);
- assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */))
+ assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(
+ () -> mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity)))
.isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH);
+ assertWithMessage("Cold launch by restart").that(launchTemplate.applyAsInt(
+ () -> mActivityMetricsLogger.notifyBindApplication(
+ mTopActivity.info.applicationInfo)))
+ .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
+
mTopActivity.app = null;
mNewActivityCreated = true;
doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid);
- assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+ assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(null))
.isEqualTo(WaitResult.LAUNCH_STATE_COLD);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index fe1ea0d99eeb..f6f3f0324f9c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -22,11 +22,17 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
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.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.app.ActivityManager.RunningTaskInfo;
@@ -41,6 +47,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.util.Log;
import android.util.Rational;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
@@ -48,7 +55,10 @@ import android.window.TaskOrganizer;
import androidx.test.filters.MediumTest;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -166,6 +176,122 @@ public class ActivityOptionsTest {
}
}
+ /**
+ * Tests if any unknown key is being used in the ActivityOptions bundle. If so, please review
+ * if the newly added bundle should be protected with permissions to avoid malicious attacks.
+ *
+ * @see SafeActivityOptionsTest#test_getOptions
+ */
+ @Test
+ public void testActivityOptionsFromBundle() {
+ // Spy on a bundle that is generated from a basic ActivityOptions.
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ Bundle bundle = options.toBundle();
+ spyOn(bundle);
+
+ // Create a new ActivityOptions from the bundle
+ new ActivityOptions(bundle);
+
+ // Verify the keys that are being used.
+ final ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
+ verify(bundle, atLeastOnce()).getString(stringCaptor.capture());
+ verify(bundle, atLeastOnce()).getBoolean(stringCaptor.capture());
+ verify(bundle, atLeastOnce()).getParcelable(stringCaptor.capture(), any());
+ verify(bundle, atLeastOnce()).getInt(stringCaptor.capture(), anyInt());
+ verify(bundle, atLeastOnce()).getBinder(stringCaptor.capture());
+ verify(bundle, atLeastOnce()).getBundle(stringCaptor.capture());
+ final List<String> keys = stringCaptor.getAllValues();
+ final List<String> unknownKeys = new ArrayList<>();
+ for (String key : keys) {
+ switch (key) {
+ case ActivityOptions.KEY_PACKAGE_NAME:
+ case ActivityOptions.KEY_LAUNCH_BOUNDS:
+ case ActivityOptions.KEY_ANIM_TYPE:
+ case ActivityOptions.KEY_ANIM_ENTER_RES_ID:
+ case ActivityOptions.KEY_ANIM_EXIT_RES_ID:
+ case ActivityOptions.KEY_ANIM_IN_PLACE_RES_ID:
+ case ActivityOptions.KEY_ANIM_BACKGROUND_COLOR:
+ case ActivityOptions.KEY_ANIM_THUMBNAIL:
+ case ActivityOptions.KEY_ANIM_START_X:
+ case ActivityOptions.KEY_ANIM_START_Y:
+ case ActivityOptions.KEY_ANIM_WIDTH:
+ case ActivityOptions.KEY_ANIM_HEIGHT:
+ case ActivityOptions.KEY_ANIM_START_LISTENER:
+ case ActivityOptions.KEY_SPLASH_SCREEN_THEME:
+ case ActivityOptions.KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE:
+ case ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN:
+ case ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN:
+ case ActivityOptions.KEY_TRANSIENT_LAUNCH:
+ case "android:activity.animationFinishedListener":
+ // KEY_ANIMATION_FINISHED_LISTENER
+ case "android:activity.animSpecs": // KEY_ANIM_SPECS
+ case "android:activity.lockTaskMode": // KEY_LOCK_TASK_MODE
+ case "android:activity.shareIdentity": // KEY_SHARE_IDENTITY
+ case "android.activity.launchDisplayId": // KEY_LAUNCH_DISPLAY_ID
+ case "android.activity.callerDisplayId": // KEY_CALLER_DISPLAY_ID
+ case "android.activity.launchTaskDisplayAreaToken":
+ // KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN
+ case "android.activity.launchTaskDisplayAreaFeatureId":
+ // KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID
+ case "android.activity.windowingMode": // KEY_LAUNCH_WINDOWING_MODE
+ case "android.activity.activityType": // KEY_LAUNCH_ACTIVITY_TYPE
+ case "android.activity.launchTaskId": // KEY_LAUNCH_TASK_ID
+ case "android.activity.disableStarting": // KEY_DISABLE_STARTING_WINDOW
+ case "android.activity.pendingIntentLaunchFlags":
+ // KEY_PENDING_INTENT_LAUNCH_FLAGS
+ case "android.activity.alwaysOnTop": // KEY_TASK_ALWAYS_ON_TOP
+ case "android.activity.taskOverlay": // KEY_TASK_OVERLAY
+ case "android.activity.taskOverlayCanResume": // KEY_TASK_OVERLAY_CAN_RESUME
+ case "android.activity.avoidMoveToFront": // KEY_AVOID_MOVE_TO_FRONT
+ case "android.activity.freezeRecentTasksReordering":
+ // KEY_FREEZE_RECENT_TASKS_REORDERING
+ case "android:activity.disallowEnterPictureInPictureWhileLaunching":
+ // KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING
+ case "android:activity.applyActivityFlagsForBubbles":
+ // KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES
+ case "android:activity.applyMultipleTaskFlagForShortcut":
+ // KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT
+ case "android:activity.applyNoUserActionFlagForShortcut":
+ // KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT
+ case "android:activity.transitionCompleteListener":
+ // KEY_TRANSITION_COMPLETE_LISTENER
+ case "android:activity.transitionIsReturning": // KEY_TRANSITION_IS_RETURNING
+ case "android:activity.sharedElementNames": // KEY_TRANSITION_SHARED_ELEMENTS
+ case "android:activity.resultData": // KEY_RESULT_DATA
+ case "android:activity.resultCode": // KEY_RESULT_CODE
+ case "android:activity.exitCoordinatorIndex": // KEY_EXIT_COORDINATOR_INDEX
+ case "android.activity.sourceInfo": // KEY_SOURCE_INFO
+ case "android:activity.usageTimeReport": // KEY_USAGE_TIME_REPORT
+ case "android:activity.rotationAnimationHint": // KEY_ROTATION_ANIMATION_HINT
+ case "android:instantapps.installerbundle": // KEY_INSTANT_APP_VERIFICATION_BUNDLE
+ case "android:activity.specsFuture": // KEY_SPECS_FUTURE
+ case "android:activity.remoteAnimationAdapter": // KEY_REMOTE_ANIMATION_ADAPTER
+ case "android:activity.remoteTransition": // KEY_REMOTE_TRANSITION
+ case "android:activity.overrideTaskTransition": // KEY_OVERRIDE_TASK_TRANSITION
+ case "android.activity.removeWithTaskOrganizer": // KEY_REMOVE_WITH_TASK_ORGANIZER
+ case "android.activity.launchTypeBubble": // KEY_LAUNCHED_FROM_BUBBLE
+ case "android.activity.splashScreenStyle": // KEY_SPLASH_SCREEN_STYLE
+ case "android.activity.launchIntoPipParams": // KEY_LAUNCH_INTO_PIP_PARAMS
+ case "android.activity.dismissKeyguard": // KEY_DISMISS_KEYGUARD
+ case "android.activity.pendingIntentCreatorBackgroundActivityStartMode":
+ // KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE
+ case "android.activity.launchCookie": // KEY_LAUNCH_COOKIE
+ // Existing keys
+ break;
+ default:
+ unknownKeys.add(key);
+ break;
+ }
+ }
+
+ // Report if any unknown key exists.
+ for (String key : unknownKeys) {
+ Log.e("ActivityOptionsTests", "Unknown key " + key + " is found. "
+ + "Please review if the given bundle should be protected with permissions.");
+ }
+ assertTrue(unknownKeys.isEmpty());
+ }
+
public static class TrampolineActivity extends Activity {
static int sTaskId;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
index 51a7e747afce..06033c7ebf75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
@@ -20,7 +20,6 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
-import static com.android.server.wm.LetterboxConfigurationPersister.LETTERBOX_CONFIGURATION_FILENAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,13 +41,26 @@ import org.junit.Test;
import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.function.Supplier;
+/**
+ * Tests for the {@link LetterboxConfigurationPersister} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:LetterboxConfigurationPersisterTest
+ */
@SmallTest
@Presubmit
public class LetterboxConfigurationPersisterTest {
private static final long TIMEOUT = 2000L; // 2 secs
+ private static final int DEFAULT_REACHABILITY_TEST = -1;
+ private static final Supplier<Integer> DEFAULT_REACHABILITY_SUPPLIER_TEST =
+ () -> DEFAULT_REACHABILITY_TEST;
+
+ private static final String LETTERBOX_CONFIGURATION_TEST_FILENAME = "letterbox_config_test";
+
private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
private Context mContext;
private PersisterQueue mPersisterQueue;
@@ -62,7 +74,7 @@ public class LetterboxConfigurationPersisterTest {
mConfigFolder = mContext.getFilesDir();
mPersisterQueue = new PersisterQueue();
mQueueState = new QueueState();
- mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(mContext,
+ mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(
() -> mContext.getResources().getInteger(
R.integer.config_letterboxDefaultPositionForHorizontalReachability),
() -> mContext.getResources().getInteger(
@@ -72,7 +84,8 @@ public class LetterboxConfigurationPersisterTest {
() -> mContext.getResources().getInteger(
R.integer.config_letterboxDefaultPositionForTabletopModeReachability
),
- mConfigFolder, mPersisterQueue, mQueueState);
+ mConfigFolder, mPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
mQueueListener = queueEmpty -> mQueueState.onItemAdded();
mPersisterQueue.addListener(mQueueListener);
mLetterboxConfigurationPersister.start();
@@ -127,8 +140,10 @@ public class LetterboxConfigurationPersisterTest {
public void test_whenUpdatedWithNewValues_valuesAreReadAfterRestart() {
final PersisterQueue firstPersisterQueue = new PersisterQueue();
final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
- mContext, () -> -1, () -> -1, () -> -1, () -> -1, mContext.getFilesDir(),
- firstPersisterQueue, mQueueState);
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), firstPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
firstPersister.start();
firstPersister.setLetterboxPositionForHorizontalReachability(false,
LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
@@ -138,8 +153,10 @@ public class LetterboxConfigurationPersisterTest {
stopPersisterSafe(firstPersisterQueue);
final PersisterQueue secondPersisterQueue = new PersisterQueue();
final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
- mContext, () -> -1, () -> -1, () -> -1, () -> -1, mContext.getFilesDir(),
- secondPersisterQueue, mQueueState);
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), secondPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
secondPersister.start();
final int newPositionForHorizontalReachability =
secondPersister.getLetterboxPositionForHorizontalReachability(false);
@@ -156,37 +173,46 @@ public class LetterboxConfigurationPersisterTest {
@Test
public void test_whenUpdatedWithNewValuesAndDeleted_valuesAreDefaults() {
- mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(false,
+ final PersisterQueue firstPersisterQueue = new PersisterQueue();
+ final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), firstPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
+ firstPersister.start();
+ firstPersister.setLetterboxPositionForHorizontalReachability(false,
LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
- mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(false,
+ firstPersister.setLetterboxPositionForVerticalReachability(false,
LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
waitForCompletion(mPersisterQueue);
final int newPositionForHorizontalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
- false);
+ firstPersister.getLetterboxPositionForHorizontalReachability(false);
final int newPositionForVerticalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
+ firstPersister.getLetterboxPositionForVerticalReachability(false);
Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
newPositionForHorizontalReachability);
Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
newPositionForVerticalReachability);
- deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue);
- waitForCompletion(mPersisterQueue);
+ deleteConfiguration(firstPersister, firstPersisterQueue);
+ waitForCompletion(firstPersisterQueue);
+ stopPersisterSafe(firstPersisterQueue);
+
+ final PersisterQueue secondPersisterQueue = new PersisterQueue();
+ final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+ mContext.getFilesDir(), secondPersisterQueue, mQueueState,
+ LETTERBOX_CONFIGURATION_TEST_FILENAME);
+ secondPersister.start();
final int positionForHorizontalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
- false);
- final int defaultPositionForHorizontalReachability =
- mContext.getResources().getInteger(
- R.integer.config_letterboxDefaultPositionForHorizontalReachability);
- Assert.assertEquals(defaultPositionForHorizontalReachability,
- positionForHorizontalReachability);
+ secondPersister.getLetterboxPositionForHorizontalReachability(false);
final int positionForVerticalReachability =
- mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
- final int defaultPositionForVerticalReachability =
- mContext.getResources().getInteger(
- R.integer.config_letterboxDefaultPositionForVerticalReachability);
- Assert.assertEquals(defaultPositionForVerticalReachability,
- positionForVerticalReachability);
+ secondPersister.getLetterboxPositionForVerticalReachability(false);
+ Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForHorizontalReachability);
+ Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForVerticalReachability);
+ deleteConfiguration(secondPersister, secondPersisterQueue);
+ waitForCompletion(secondPersisterQueue);
+ stopPersisterSafe(secondPersisterQueue);
}
private void stopPersisterSafe(PersisterQueue persisterQueue) {
@@ -222,7 +248,7 @@ public class LetterboxConfigurationPersisterTest {
private void deleteConfiguration(LetterboxConfigurationPersister persister,
PersisterQueue persisterQueue) {
final AtomicFile fileToDelete = new AtomicFile(
- new File(mConfigFolder, LETTERBOX_CONFIGURATION_FILENAME));
+ new File(mConfigFolder, LETTERBOX_CONFIGURATION_TEST_FILENAME));
persisterQueue.addItem(
new DeleteFileCommand(fileToDelete, mQueueState.andThen(
s -> persister.useDefaultValue())), true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index f23e56df2580..7cb58022c0e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1303,6 +1303,26 @@ public class RecentTasksTest extends WindowTestsBase {
assertTrue(info.supportsMultiWindow);
}
+ @Test
+ public void testRemoveCompatibleRecentTask() {
+ final Task task1 = createTaskBuilder(".Task").setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN).build();
+ mRecentTasks.add(task1);
+ final Task task2 = createTaskBuilder(".Task").setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW).build();
+ mRecentTasks.add(task2);
+ assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+ true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+
+ // Set windowing mode and ensure the same fullscreen task that created earlier is removed.
+ task2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mRecentTasks.removeCompatibleRecentTask(task2);
+ assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+ true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+ assertEquals(task2.mTaskId, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+ true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().get(0).taskId);
+ }
+
private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) {
HardwareBuffer buffer = null;
if (bufferSize != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index abf21a57dd40..7eab06ac8b95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -656,7 +656,7 @@ public class RootTaskTests extends WindowTestsBase {
topSplitPrimary.getVisibility(null /* starting */));
// Make primary split root transient-hide.
spyOn(splitPrimary.mTransitionController);
- doReturn(true).when(splitPrimary.mTransitionController).isTransientHide(
+ doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible(
organizer.mPrimary);
// The split root and its top become visible.
assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 24e932f36f80..6c48a6961bc2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -16,17 +16,36 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.os.Looper;
import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationAdapter;
+import android.window.RemoteTransition;
import android.window.WindowContainerToken;
import androidx.test.filters.MediumTest;
import org.junit.Test;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
/**
* Build/Install/Run:
@@ -73,4 +92,119 @@ public class SafeActivityOptionsTest {
assertSame(clone.getOriginalOptions().getLaunchRootTask(), token);
}
+
+ @Test
+ public void test_getOptions() {
+ // Mock everything necessary
+ MockitoSession mockingSession = mockitoSession()
+ .mockStatic(ActivityTaskManagerService.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ doReturn(PERMISSION_DENIED).when(() -> ActivityTaskManagerService.checkPermission(
+ any(), anyInt(), anyInt()));
+
+ final LockTaskController lockTaskController = mock(LockTaskController.class);
+ doReturn(false).when(lockTaskController).isPackageAllowlisted(anyInt(), any());
+
+ final ActivityTaskManagerService atm = mock(ActivityTaskManagerService.class);
+ doReturn(lockTaskController).when(atm).getLockTaskController();
+
+ final ActivityTaskSupervisor taskSupervisor =
+ new ActivityTaskSupervisor(atm, mock(Looper.class));
+ spyOn(taskSupervisor);
+ doReturn(false).when(taskSupervisor).isCallerAllowedToLaunchOnDisplay(anyInt(),
+ anyInt(), anyInt(), any());
+ doReturn(false).when(taskSupervisor).isCallerAllowedToLaunchOnTaskDisplayArea(anyInt(),
+ anyInt(), any(), any());
+
+ taskSupervisor.mRecentTasks = mock(RecentTasks.class);
+ doReturn(false).when(taskSupervisor.mRecentTasks).isCallerRecents(anyInt());
+
+ // Ensure exceptions are thrown when lack of permissions.
+ ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ try {
+ activityOptions.setLaunchTaskId(100);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setDisableStartingWindow(true);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setTransientLaunch();
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setDismissKeyguard();
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchedFromBubble(true);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchDisplayId(DEFAULT_DISPLAY);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLockTaskEnabled(true);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeCustomTaskAnimation(
+ getInstrumentation().getContext(), 0, 0, null, null, null);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ RemoteAnimationAdapter remoteAnimationAdapter = mock(RemoteAnimationAdapter.class);
+ RemoteTransition remoteTransition = mock(RemoteTransition.class);
+ activityOptions = ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter,
+ remoteTransition);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setRemoteAnimationAdapter(remoteAnimationAdapter);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeRemoteTransition(remoteTransition);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setRemoteTransition(remoteTransition);
+ verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+ verifySecureExceptionThrown(activityOptions, taskSupervisor,
+ mock(TaskDisplayArea.class));
+ } finally {
+ mockingSession.finishMocking();
+ }
+ }
+
+ private void verifySecureExceptionThrown(ActivityOptions activityOptions,
+ ActivityTaskSupervisor taskSupervisor) {
+ verifySecureExceptionThrown(activityOptions, taskSupervisor, null /* mockTda */);
+ }
+
+ private void verifySecureExceptionThrown(ActivityOptions activityOptions,
+ ActivityTaskSupervisor taskSupervisor, TaskDisplayArea mockTda) {
+ SafeActivityOptions safeActivityOptions = new SafeActivityOptions(activityOptions);
+ if (mockTda != null) {
+ spyOn(safeActivityOptions);
+ doReturn(mockTda).when(safeActivityOptions).getLaunchTaskDisplayArea(any(), any());
+ }
+
+ boolean isExceptionThrow = false;
+ final ActivityInfo aInfo = mock(ActivityInfo.class);
+ try {
+ safeActivityOptions.getOptions(null, aInfo, null, taskSupervisor);
+ } catch (SecurityException ex) {
+ isExceptionThrow = true;
+ }
+ assertTrue(isExceptionThrow);
+ }
}
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 3908947804cd..d5afe3b2f078 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -27,6 +27,11 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+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_SPLIT_SCREEN;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
@@ -91,9 +96,12 @@ import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -2255,6 +2263,169 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+ }
+
+ @Test
+ public void testUserOverrideSplitScreenAspectRatioForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+ }
+
+ @Test
+ public void testUserOverrideDisplaySizeAspectRatioForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayWidth / displayHeight;
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+ }
+
+ @Test
+ public void testUserOverrideDisplaySizeAspectRatioForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+ float expectedAspectRatio = 1f * displayHeight / displayWidth;
+
+ testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+ }
+
+ @Test
+ public void testUserOverride32AspectRatioForPortraitDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
+ testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
+ }
+
+ @Test
+ public void testUserOverride32AspectRatioForLandscapeDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+ testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
+ }
+
+ @Test
+ public void testUserOverride43AspectRatioForPortraitDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
+ testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
+ }
+
+ @Test
+ public void testUserOverride43AspectRatioForLandscapeDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+ testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
+ }
+
+ @Test
+ public void testUserOverride169AspectRatioForPortraitDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1800, /* dh */ 1500);
+ testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
+ }
+
+ @Test
+ public void testUserOverride169AspectRatioForLandscapeDisplay() {
+ setUpDisplaySizeWithApp(/* dw */ 1500, /* dh */ 1800);
+ testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testUserOverrideAspectRatioOverSystemOverride() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_PORTRAIT,
+ 3 / 2f,
+ USER_MIN_ASPECT_RATIO_3_2,
+ true);
+ }
+
+ @Test
+ public void testUserOverrideAspectRatioNotEnabled() {
+ setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+
+ // App aspect ratio doesn't change
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_PORTRAIT,
+ 1f * 1600 / 1400,
+ USER_MIN_ASPECT_RATIO_3_2,
+ false);
+ }
+
+ private void testUserOverrideAspectRatio(float expectedAspectRatio,
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ testUserOverrideAspectRatio(true,
+ SCREEN_ORIENTATION_PORTRAIT,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_PORTRAIT,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+
+ testUserOverrideAspectRatio(true,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+
+ testUserOverrideAspectRatio(false,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ expectedAspectRatio,
+ aspectRatio,
+ true);
+ }
+
+ private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
+ float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
+ boolean enabled) {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+ activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.mWmService.mLetterboxConfiguration
+ .setUserAppAspectRatioSettingsOverrideEnabled(enabled);
+ // Set user aspect ratio override
+ final IPackageManager pm = mAtm.getPackageManager();
+ try {
+ doReturn(aspectRatio).when(pm)
+ .getUserMinAspectRatio(activity.packageName, activity.mUserId);
+ } catch (RemoteException ignored) {
+ }
+
+ prepareLimitedBounds(activity, screenOrientation, isUnresizable);
+
+ final Rect afterBounds = activity.getBounds();
+ final int width = afterBounds.width();
+ final int height = afterBounds.height();
+ final float afterAspectRatio =
+ (float) Math.max(width, height) / (float) Math.min(width, height);
+
+ assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
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 ffecafb7b4ed..5154d17f2e6b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1488,6 +1488,47 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testIsTransientVisible() {
+ final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task taskA = appA.getTask();
+ final Task taskB = appB.getTask();
+ final Task taskRecent = recent.getTask();
+ registerTestTransitionPlayer();
+ final TransitionController controller = mRootWindowContainer.mTransitionController;
+ final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
+ controller.moveToCollecting(transition);
+ transition.collect(recent);
+ transition.collect(taskA);
+ transition.setTransientLaunch(recent, taskA);
+ taskRecent.moveToFront("move-recent-to-front");
+
+ // During collecting and playing, the recent is on top so it is visible naturally.
+ // While B needs isTransientVisible to keep visibility because it is occluded by recents.
+ assertFalse(controller.isTransientVisible(taskB));
+ assertTrue(controller.isTransientVisible(taskA));
+ assertFalse(controller.isTransientVisible(taskRecent));
+ // Switch to playing state.
+ transition.onTransactionReady(transition.getSyncId(), mMockT);
+ assertTrue(controller.isTransientVisible(taskA));
+
+ // Switch to another task. For example, use gesture navigation to switch tasks.
+ taskB.moveToFront("move-b-to-front");
+ // The previous app (taskA) should be paused first so it loses transient visible. Because
+ // visually it is taskA -> taskB, the pause -> resume order should be the same.
+ assertFalse(controller.isTransientVisible(taskA));
+ // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
+ // to avoid the latency to resume the current top, i.e. appB.
+ assertTrue(controller.isTransientVisible(taskRecent));
+ // The recent is paused after the transient transition is finished.
+ controller.finishTransition(transition);
+ assertFalse(controller.isTransientVisible(taskRecent));
+ }
+
+ @Test
public void testNotReadyPushPop() {
final TransitionController controller = new TestTransitionController(mAtm);
controller.setSyncEngine(mWm.mSyncEngine);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index efcbd31fb6e4..f1ee76e1ed94 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5268,7 +5268,11 @@ public class TelephonyManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+ String[] mergedImsisFromGroup =
+ telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+ if (mergedImsisFromGroup != null) {
+ return mergedImsisFromGroup;
+ }
}
} catch (RemoteException ex) {
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index 144a73121746..6722cc8fd021 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -62,14 +62,6 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
}
}
- /** Assert the background animation layer is never visible during bounds change transition. */
- @Presubmit
- @Test
- fun backgroundLayerNeverVisible() {
- val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
- flicker.assertLayers { isInvisible(backgroundColorLayer) }
- }
-
/** Trampoline activity should finish itself before the end of this test. */
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
new file mode 100644
index 000000000000..0417f9dbb4bf
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
+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 androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a secondary Activity into Picture-In-Picture mode.
+ *
+ * Setup: Start from a split A|B.
+ * Transition: B enters PIP, observe the window shrink to the bottom right corner on screen.
+ *
+ * To run this test: `atest FlickerTests:SecondaryActivityEnterPipTest`
+ *
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SecondaryActivityEnterPipTest (flicker: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
+ }
+ transitions {
+ testApp.secondaryActivityEnterPip(wmHelper)
+ }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /**
+ * Main and secondary activity start from a split each taking half of the screen.
+ */
+ @Presubmit
+ @Test
+ fun layersStartFromEqualSplit() {
+ flicker.assertLayersStart {
+ val leftLayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val rightLayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ // Compare dimensions of two splits, given we're using default split attributes,
+ // both activities take up the same visible size on the display.
+ check { "height" }
+ .that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height)
+ check { "width" }
+ .that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width)
+ leftLayerRegion.notOverlaps(rightLayerRegion.region)
+ leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
+ }
+ flicker.assertLayersEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Main Activity is visible throughout the transition and becomes fullscreen.
+ */
+ @Presubmit
+ @Test
+ fun mainActivityWindowBecomesFullScreen() {
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ flicker.assertWmEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Main Activity is visible throughout the transition and becomes fullscreen.
+ */
+ @Presubmit
+ @Test
+ fun mainActivityLayerBecomesFullScreen() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(TRANSITION_SNAPSHOT)
+ .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ }
+ flicker.assertLayersEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Secondary Activity is visible throughout the transition and shrinks to the bottom right
+ * corner.
+ */
+ @Presubmit
+ @Test
+ fun secondaryWindowShrinks() {
+ flicker.assertWm {
+ isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ flicker.assertWmEnd {
+ val pipWindowRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ check{"height"}
+ .that(pipWindowRegion.region.height)
+ .isLower(startDisplayBounds.height / 2)
+ check{"width"}
+ .that(pipWindowRegion.region.width).isLower(startDisplayBounds.width)
+ }
+ }
+
+ /**
+ * During the transition Secondary Activity shrinks to the bottom right corner.
+ */
+ @Presubmit
+ @Test
+ fun secondaryLayerShrinks() {
+ flicker.assertLayers {
+ val pipLayerList = layers {
+ ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
+ }
+ pipLayerList.zipWithNext { previous, current ->
+ // TODO(b/290987990): Add checks for visibleRegion.
+ current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+ current.screenBounds.notBiggerThan(previous.screenBounds.region)
+ }
+ }
+ flicker.assertLayersEnd {
+ val pipRegion = visibleRegion(
+ ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ check { "height" }
+ .that(pipRegion.region.height)
+ .isLower(startDisplayBounds.height / 2)
+ check { "width" }
+ .that(pipRegion.region.width).isLower(startDisplayBounds.width)
+ }
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index a72ec1e678b3..2d3bc2d5eb15 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -113,6 +113,21 @@ constructor(
.waitForAndVerify()
}
+ fun secondaryActivityEnterPip(wmHelper: WindowManagerStateHelper) {
+ val pipButton =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "secondary_enter_pip_button")),
+ FIND_TIMEOUT
+ )
+ require(pipButton != null) { "Can't find enter pip button on screen." }
+ pipButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withPipShown()
+ .waitForAndVerify()
+ }
+
/**
* Clicks the button to launch a secondary activity with alwaysExpand enabled, which will launch
* a fullscreen window on top of the visible region.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index c1db4231aa87..ea7c7c4276dc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.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 androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.setRotation
@@ -78,6 +79,7 @@ class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyF
flicker.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp)
}
+ @FlakyTest(bugId = 290767483)
@Postsubmit
@Test
fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 7a16060e3370..94b090f42c9b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -200,7 +200,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) :
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 251217585)
+ @FlakyTest(bugId = 285980483)
@Test
override fun focusChanges() {
super.focusChanges()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 4164c0d440c0..df9780ef8b98 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,6 +21,7 @@ import android.app.WallpaperManager
import android.content.res.Resources
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -190,6 +191,16 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp)
+ )
+ }
+ }
+
companion object {
private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 68ae806f3c8b..ff9799a1c710 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -102,6 +102,24 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".LaunchTransparentActivity"
+ android:resizeableActivity="false"
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
+ android:label="LaunchTransparentActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".TransparentActivity"
+ android:theme="@style/TransparentTheme"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.TransparentActivity"
+ android:label="TransparentActivity"
+ android:exported="false">
+ </activity>
<activity android:name=".LaunchNewActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
android:theme="@style/CutoutShortEdges"
@@ -206,6 +224,7 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:supportsPictureInPicture="true"
android:exported="false"/>
<activity
android:name=".ActivityEmbeddingThirdActivity"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
index 67314463161d..135140aa2377 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
@@ -35,4 +35,10 @@
android:onClick="launchThirdActivity"
android:text="Launch a third activity" />
+ <Button
+ android:id="@+id/secondary_enter_pip_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="Enter pip" />
+
</LinearLayout> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
new file mode 100644
index 000000000000..0730ded66ce4
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</FrameLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
new file mode 100644
index 000000000000..ff4ead95f16e
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/black">
+
+ <Button
+ android:id="@+id/button_launch_transparent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Launch Transparent" />
+ <Button
+ android:id="@+id/button_request_permission"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Request Permission" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 1d21fd56a487..e51ed29adebf 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -43,6 +43,13 @@
<item name="android:windowSoftInputMode">stateUnchanged</item>
</style>
+ <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
+
<style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowDisablePreview">true</item>
</style>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index dc21027bc99c..ee087ef9be2c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.content.Intent;
+import android.app.PictureInPictureParams;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
@@ -40,6 +41,16 @@ public class ActivityEmbeddingSecondaryActivity extends Activity {
finish();
}
});
+ findViewById(R.id.secondary_enter_pip_button).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PictureInPictureParams.Builder picInPicParamsBuilder =
+ new PictureInPictureParams.Builder();
+ enterPictureInPictureMode(picInPicParamsBuilder.build());
+ }
+ }
+ );
}
public void launchThirdActivity(View view) {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 95c86acb9ee9..2795a6c43015 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -73,6 +73,18 @@ public class ActivityOptions {
FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
}
+ public static class TransparentActivity {
+ public static final String LABEL = "TransparentActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".TransparentActivity");
+ }
+
+ public static class LaunchTransparentActivity {
+ public static final String LABEL = "LaunchTransparentActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchTransparentActivity");
+ }
+
public static class DialogThemedActivity {
public static final String LABEL = "DialogThemedActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java
new file mode 100644
index 000000000000..7c161fd8e611
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LaunchTransparentActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_transparent_launch);
+ findViewById(R.id.button_launch_transparent)
+ .setOnClickListener(v -> launchTransparentActivity());
+ }
+
+ private void launchTransparentActivity() {
+ startActivity(new Intent(this, TransparentActivity.class));
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
new file mode 100644
index 000000000000..1bac8bdb003a
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.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 com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class TransparentActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_transparent);
+ }
+}
diff --git a/tests/permission/OWNERS b/tests/permission/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/tests/permission/OWNERS
+++ b/tests/permission/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
index e03d92ab44a0..f1727b78f135 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -30,6 +30,7 @@ const val BINDER_CLASS = "android.os.Binder"
const val IINTERFACE_INTERFACE = "android.os.IInterface"
const val AIDL_PERMISSION_HELPER_SUFFIX = "_enforcePermission"
+const val PERMISSION_PREFIX_LITERAL = "android.permission."
/**
* If a non java (e.g. c++) backend is enabled, the @EnforcePermission
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 0a66cd510353..3a95df9b2773 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
@@ -95,7 +95,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
val v1 = ConstantEvaluator.evaluate(context, value1)
val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != null && v2 != null) {
- if (v1 != v2) {
+ if (v1 != v2 && !isOneShortPermissionOfOther(v1, v2)) {
return false
}
} else {
@@ -107,7 +107,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
for (j in children1.indices) {
val c1 = ConstantEvaluator.evaluate(context, children1[j])
val c2 = ConstantEvaluator.evaluate(context, children2[j])
- if (c1 != c2) {
+ if (c1 != c2 && !isOneShortPermissionOfOther(c1, c2)) {
return false
}
}
@@ -116,6 +116,12 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
return true
}
+ private fun isOneShortPermissionOfOther(
+ permission1: Any?,
+ permission2: Any?
+ ): Boolean = permission1 == (permission2 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL) ||
+ permission2 == (permission1 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL)
+
private fun compareMethods(
context: JavaContext,
element: UElement,
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 75b00737a168..b3dacbdf57a6 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
@@ -316,20 +316,55 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
public void testMethod() {}
~~~~~~~~~~
- 1 errors, 0 warnings
+ 1 errors, 0 warnings
""".addLineContinuation()
)
}
- fun testDoesDetectIssuesShortStringsNotAllowed() {
+ fun testDoesNotDetectIssuesShortStringsAllowedInChildAndParent() {
lint().files(java(
"""
package test.pkg;
import android.annotation.EnforcePermission;
public class TestClass121 extends IFooMethod.Stub {
@Override
+ @EnforcePermission("READ_PHONE_STATE")
+ public void testMethod() {}
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethodParentShortPermission() {}
+ @Override
@EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesDetectIssuesWrongShortStringsInChildAndParent() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass121 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission("READ_WRONG_PHONE_STATE")
+ public void testMethod() {}
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE)
+ public void testMethodParentShortPermission() {}
+ @Override
+ @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteral() {}
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {}
}
""").indented(),
*stubs
@@ -337,14 +372,19 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/test/pkg/TestClass121.java:6: Error: The method \
- TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAnyLiteral: \
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
- The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAnyLiteral() {}
- ~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ src/test/pkg/TestClass121.java:6: Error: The method TestClass121.testMethod is annotated with @EnforcePermission("READ_WRONG_PHONE_STATE") which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+ src/test/pkg/TestClass121.java:9: Error: The method TestClass121.testMethodParentShortPermission is annotated with @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE) which differs from the overridden method Stub.testMethodParentShortPermission: @android.annotation.EnforcePermission("READ_PHONE_STATE"). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodParentShortPermission() {}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:12: Error: The method TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"}) which differs from the overridden method Stub.testMethodAnyLiteral: @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteral() {}
+ ~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:15: Error: The method TestClass121.testMethodAnyLiteralParentsShortPermission is annotated with @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE}) which differs from the overridden method Stub.testMethodAnyLiteralParentsShortPermission: @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteralParentsShortPermission() {}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 4 errors, 0 warnings
""".addLineContinuation()
)
}
@@ -360,12 +400,18 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
public void testMethod() {}
@Override
+ @android.annotation.EnforcePermission("READ_PHONE_STATE")
+ public void testMethodParentShortPermission() {}
+ @Override
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAny() {}
@Override
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
@Override
+ @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteralParentsShortPermission() {}
+ @Override
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAll() {}
@Override
@@ -374,10 +420,14 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
}
@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
public void testMethod();
+ @android.annotation.EnforcePermission("READ_PHONE_STATE")
+ public void testMethodParentShortPermission();
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAny() {}
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
+ @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteralParentsShortPermission() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAll() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
@@ -404,6 +454,7 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package android.Manifest;
class permission {
public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String READ_WRONG_PHONE_STATE = "android.permission.READ_WRONG_PHONE_STATE";
public static final String NFC = "android.permission.NFC";
public static final String INTERNET = "android.permission.INTERNET";
}