summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp28
-rw-r--r--api/StubLibraries.bp9
-rw-r--r--core/api/current.txt23
-rw-r--r--core/api/module-lib-current.txt24
-rw-r--r--core/api/removed.txt5
-rw-r--r--core/api/system-current.txt168
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/app/ActivityThread.java8
-rw-r--r--core/java/android/app/ApplicationStartInfo.java2
-rw-r--r--core/java/android/app/ClientTransactionHandler.java4
-rw-r--r--core/java/android/app/GrammaticalInflectionManager.java53
-rw-r--r--core/java/android/app/IGrammaticalInflectionManager.aidl12
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/Notification.java46
-rw-r--r--core/java/android/app/OWNERS3
-rw-r--r--core/java/android/app/UiAutomationConnection.java11
-rw-r--r--core/java/android/app/activity_manager.aconfig8
-rw-r--r--core/java/android/app/assist/AssistStructure.java33
-rw-r--r--core/java/android/app/grammatical_inflection_manager.aconfig8
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java18
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java7
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java41
-rw-r--r--core/java/android/companion/virtual/VirtualDevice.java26
-rw-r--r--core/java/android/companion/virtual/flags.aconfig7
-rw-r--r--core/java/android/hardware/display/DisplayManager.java12
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java30
-rw-r--r--core/java/android/hardware/radio/ProgramSelector.java4
-rw-r--r--core/java/android/hardware/radio/flags.aconfig8
-rw-r--r--core/java/android/net/metrics/WakeupEvent.java4
-rw-r--r--core/java/android/net/network-policy-restrictions.md4
-rw-r--r--core/java/android/os/BinderProxy.java28
-rw-r--r--core/java/android/os/BugreportParams.java2
-rw-r--r--core/java/android/os/UserManager.java12
-rw-r--r--core/java/android/permission/flags.aconfig7
-rw-r--r--core/java/android/provider/Settings.java48
-rw-r--r--core/java/android/security/FileIntegrityManager.java27
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java52
-rw-r--r--core/java/android/text/ClientFlags.java58
-rw-r--r--core/java/android/text/TextFlags.java29
-rw-r--r--core/java/android/view/DisplayEventReceiver.java15
-rw-r--r--core/java/android/view/DisplayInfo.java13
-rw-r--r--core/java/android/view/IWindowManager.aidl8
-rw-r--r--core/java/android/view/TextureView.java20
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--core/java/android/view/ViewRootImpl.java61
-rw-r--r--core/java/android/widget/TextView.java3
-rw-r--r--core/java/android/window/StartingWindowInfo.java4
-rw-r--r--core/java/android/window/TransitionRequestInfo.java21
-rw-r--r--core/java/com/android/internal/content/PackageMonitor.java32
-rw-r--r--core/java/com/android/internal/net/OWNERS2
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl1
-rw-r--r--core/java/com/android/internal/view/RotationPolicy.java20
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java41
-rw-r--r--core/java/com/android/server/net/OWNERS2
-rw-r--r--core/jni/Android.bp21
-rw-r--r--core/jni/android_graphics_SurfaceTexture.cpp151
-rw-r--r--core/jni/android_util_Binder.cpp140
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp26
-rw-r--r--core/res/res/values/config.xml24
-rw-r--r--core/res/res/values/config_telephony.xml31
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java46
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java99
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java7
-rw-r--r--core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java75
-rw-r--r--data/etc/services.core.protolog.json18
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java90
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java75
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java68
-rw-r--r--keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java11
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java3
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java34
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java19
-rw-r--r--keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java17
-rw-r--r--keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java59
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt52
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java22
-rw-r--r--libs/hwui/Mesh.h5
-rw-r--r--libs/hwui/RecordingCanvas.cpp5
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.cpp12
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.h1
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp3
-rw-r--r--libs/input/tests/Android.bp15
-rw-r--r--libs/input/tests/PointerController_test.cpp46
-rw-r--r--media/java/android/media/AudioAttributes.java89
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java5
-rw-r--r--mime/Android.bp5
-rw-r--r--packages/CredentialManager/shared/Android.bp1
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt19
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt15
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt53
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt8
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt20
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt47
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt66
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt26
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt25
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt45
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt162
-rw-r--r--packages/CredentialManager/wear/Android.bp1
-rw-r--r--packages/CredentialManager/wear/AndroidManifest.xml31
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt100
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt32
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt89
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt17
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt87
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt123
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt27
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt4
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt76
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt51
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt35
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt (renamed from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt)12
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt (renamed from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt)2
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt (renamed from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt)3
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt (renamed from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt)66
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt154
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java8
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java5
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java1
-rw-r--r--packages/SettingsLib/tests/integ/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/tests/integ/AndroidTest.xml2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java23
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java23
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java2
-rw-r--r--packages/SystemUI/Android.bp3
-rw-r--r--packages/SystemUI/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/TEST_MAPPING17
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt51
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt37
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt11
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt5
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt43
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt21
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt31
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt25
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt6
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt53
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt190
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt157
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt13
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt75
-rw-r--r--packages/SystemUI/res/drawable/volume_row_seekbar.xml15
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml31
-rw-r--r--packages/SystemUI/res/values-land/config.xml2
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/ids.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java45
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java (renamed from packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt (renamed from packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt (renamed from packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt (renamed from packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt (renamed from packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java (renamed from packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt (renamed from packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt (renamed from packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java369
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java (renamed from packages/SystemUI/src/com/android/systemui/qs/QSFragment.java)154
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt160
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt186
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt252
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt232
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java253
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java112
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt)24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java)285
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt141
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt220
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt290
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt351
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt94
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java147
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt254
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java4
-rw-r--r--services/accessibility/accessibility.aconfig11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java16
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java56
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java88
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java4
-rw-r--r--services/companion/java/com/android/server/companion/securechannel/SecureChannel.java3
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java13
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java25
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java18
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java2
-rw-r--r--services/core/java/com/android/server/am/Android.bp10
-rw-r--r--services/core/java/com/android/server/am/AppBatteryTracker.java28
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java2
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java10
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java4
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig9
-rw-r--r--services/core/java/com/android/server/connectivity/OWNERS2
-rw-r--r--services/core/java/com/android/server/display/BrightnessRangeController.java30
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java19
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java3
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java67
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java29
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java72
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerControllerInterface.java4
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java114
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java13
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java105
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java52
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig40
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java236
-rw-r--r--services/core/java/com/android/server/display/mode/Vote.java140
-rw-r--r--services/core/java/com/android/server/display/mode/VotesStorage.java15
-rw-r--r--services/core/java/com/android/server/display/state/DisplayStateController.java8
-rw-r--r--services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java173
-rw-r--r--services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java166
-rw-r--r--services/core/java/com/android/server/grammaticalinflection/OWNERS1
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java12
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java25
-rw-r--r--services/core/java/com/android/server/net/OWNERS2
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java19
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java35
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java11
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java2
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java284
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java714
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java24
-rw-r--r--services/core/java/com/android/server/pm/DistractingPackageHelper.java43
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java275
-rw-r--r--services/core/java/com/android/server/pm/InstallingSession.java22
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerException.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java242
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceInjector.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java31
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java122
-rw-r--r--services/core/java/com/android/server/pm/PackageSender.java17
-rw-r--r--services/core/java/com/android/server/pm/PreferredActivityHelper.java18
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java19
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java2
-rw-r--r--services/core/java/com/android/server/pm/StorageEventHelper.java26
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java96
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING13
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java6
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java22
-rw-r--r--services/core/java/com/android/server/security/rkp/OWNERS2
-rw-r--r--services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java11
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarShellCommand.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java65
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java55
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotationReversionController.java3
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java18
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java46
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java18
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java202
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig6
-rw-r--r--services/foldables/devicestateprovider/Android.bp13
-rw-r--r--services/foldables/devicestateprovider/OWNERS2
-rw-r--r--services/foldables/devicestateprovider/README.md3
-rw-r--r--services/foldables/devicestateprovider/TEST_MAPPING12
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java537
-rw-r--r--services/foldables/devicestateprovider/tests/Android.bp30
-rw-r--r--services/foldables/devicestateprovider/tests/AndroidManifest.xml (renamed from packages/CredentialManager/wear/res/values/themes.xml)24
-rw-r--r--services/foldables/devicestateprovider/tests/AndroidTest.xml29
-rw-r--r--services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java519
-rw-r--r--services/net/OWNERS2
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java23
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt2
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt5
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt430
-rw-r--r--services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt106
-rw-r--r--services/tests/RemoteProvisioningServiceTests/OWNERS2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java101
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java113
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java107
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java84
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java101
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java148
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java446
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java13
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java26
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt19
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt51
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java31
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt96
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java890
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java135
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java92
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java6
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java20
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java80
-rw-r--r--telephony/java/android/telephony/satellite/stub/ISatellite.aidl10
-rw-r--r--telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java19
-rw-r--r--tests/BinderLeakTest/Android.bp40
-rw-r--r--tests/BinderLeakTest/AndroidManifest.xml17
-rw-r--r--tests/BinderLeakTest/AndroidManifest_legacy.xml20
-rw-r--r--tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl5
-rw-r--r--tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl10
-rw-r--r--tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java153
-rw-r--r--tests/BinderLeakTest/java/com/android/test/binder/MyService.java63
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java4
-rw-r--r--tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java65
-rw-r--r--tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt6
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt34
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt17
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt20
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt9
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt53
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt12
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt7
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt17
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt98
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp66
-rwxr-xr-xtools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh2
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt680
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt512
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt968
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt837
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt2348
-rwxr-xr-xtools/hoststubgen/scripts/dump-jar2
-rwxr-xr-xtools/hoststubgen/scripts/run-all-tests.sh1
-rw-r--r--tools/lint/fix/soong_lint_fix.py163
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt2
-rw-r--r--tools/lint/utils/enforce_permission_counter.py73
551 files changed, 21284 insertions, 5883 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a271d063d1f9..b1f587e45a6d 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
aconfig_srcjars = [
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+ ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
@@ -36,6 +37,7 @@ aconfig_srcjars = [
":hwui_flags_java_lib{.generated_srcjars}",
":display_flags_lib{.generated_srcjars}",
":android.multiuser.flags-aconfig-java{.generated_srcjars}",
+ ":android.app.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -327,3 +329,29 @@ java_aconfig_library {
aconfig_declarations: "android.multiuser.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Activity Manager
+aconfig_declarations {
+ name: "android.app.flags-aconfig",
+ package: "android.app",
+ srcs: ["core/java/android/app/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.flags-aconfig-java",
+ aconfig_declarations: "android.app.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Broadcast Radio
+aconfig_declarations {
+ name: "android.hardware.radio.flags-aconfig",
+ package: "android.hardware.radio",
+ srcs: ["core/java/android/hardware/radio/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.radio.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.radio.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 56a69b46b12c..e5e0ad377b03 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -86,6 +86,9 @@ module_libs = " --show-annotation android.annotation.SystemApi\\(" +
droidstubs {
name: "system-api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -126,6 +129,9 @@ droidstubs {
droidstubs {
name: "test-api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -173,6 +179,9 @@ droidstubs {
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
+ srcs: [
+ ":framework-minus-apex-aconfig-srcjars",
+ ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
diff --git a/core/api/current.txt b/core/api/current.txt
index 22b1ef8ecd20..43c8214e9749 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4616,9 +4616,9 @@ package android.app {
public class ActivityManager {
method public int addAppTask(@NonNull android.app.Activity, @NonNull android.content.Intent, @Nullable android.app.ActivityManager.TaskDescription, @NonNull android.graphics.Bitmap);
- method public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
+ method @FlaggedApi("android.app.app_start_info") public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
method public void appNotResponding(@NonNull String);
- method public void clearApplicationStartInfoCompletionListener();
+ method @FlaggedApi("android.app.app_start_info") public void clearApplicationStartInfoCompletionListener();
method public boolean clearApplicationUserData();
method public void clearWatchHeapLimit();
method @RequiresPermission(android.Manifest.permission.DUMP) public void dumpPackageState(java.io.FileDescriptor, String);
@@ -4626,7 +4626,7 @@ package android.app {
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
- method @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
+ method @FlaggedApi("android.app.app_start_info") @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
method public int getLargeMemoryClass();
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
@@ -4653,7 +4653,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle);
method @Deprecated public void restartPackage(String);
- method public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
+ method @FlaggedApi("android.app.app_start_info") public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
method public void setProcessStateSummary(@Nullable byte[]);
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
@@ -5234,7 +5234,7 @@ package android.app {
field public static final int REASON_USER_STOPPED = 11; // 0xb
}
- public final class ApplicationStartInfo implements android.os.Parcelable {
+ @FlaggedApi("android.app.app_start_info") public final class ApplicationStartInfo implements android.os.Parcelable {
method public int describeContents();
method public int getDefiningUid();
method @Nullable public android.content.Intent getIntent();
@@ -5952,6 +5952,7 @@ package android.app {
public class GrammaticalInflectionManager {
method public int getApplicationGrammaticalGender();
+ method @FlaggedApi("android.app.system_terms_of_address_enabled") public int getSystemGrammaticalGender();
method public void setRequestedApplicationGrammaticalGender(int);
}
@@ -6749,7 +6750,6 @@ package android.app {
method public android.app.Notification.WearableExtender clone();
method public android.app.Notification.Builder extend(android.app.Notification.Builder);
method public java.util.List<android.app.Notification.Action> getActions();
- method @Deprecated public android.graphics.Bitmap getBackground();
method public String getBridgeTag();
method public int getContentAction();
method @Deprecated public int getContentIcon();
@@ -6768,7 +6768,6 @@ package android.app {
method @Deprecated public boolean getHintShowBackgroundOnly();
method @Deprecated public java.util.List<android.app.Notification> getPages();
method public boolean getStartScrollBottom();
- method @Deprecated public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
method public android.app.Notification.WearableExtender setBridgeTag(String);
method public android.app.Notification.WearableExtender setContentAction(int);
method @Deprecated public android.app.Notification.WearableExtender setContentIcon(int);
@@ -9682,6 +9681,7 @@ package android.companion.virtual {
method public int describeContents();
method public int getDeviceId();
method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @NonNull public int[] getDisplayIds();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public CharSequence getDisplayName();
method @Nullable public String getName();
method @Nullable public String getPersistentDeviceId();
method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomSensorSupport();
@@ -39008,6 +39008,7 @@ package android.security.keystore {
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
method public int getMaxUsageCount();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -39015,6 +39016,7 @@ package android.security.keystore {
method public boolean isDevicePropertiesAttestationIncluded();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isStrongBoxBacked();
method public boolean isUnlockedDeviceRequired();
@@ -39046,6 +39048,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -39150,12 +39153,14 @@ package android.security.keystore {
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method public int getMaxUsageCount();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isUnlockedDeviceRequired();
method public boolean isUserAuthenticationRequired();
@@ -39177,6 +39182,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
+ method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
@@ -42898,7 +42904,7 @@ package android.telephony {
field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
- field public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_SUPPORTS_TETHERING_BOOL = "carrier_supports_tethering_bool";
@@ -43066,6 +43072,7 @@ package android.telephony {
field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9b8d2b496a30..052d614fa5fc 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -556,24 +556,24 @@ package android.provider {
package android.se.omapi {
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeFrameworkInitializer {
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public class SeFrameworkInitializer {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeServiceManager {
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public class SeServiceManager {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public SeServiceManager.ServiceNotFoundException(@NonNull String);
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public SeServiceManager.ServiceNotFoundException(@NonNull String);
}
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static final class SeServiceManager.ServiceRegisterer {
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder get();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void register(@NonNull android.os.IBinder);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder tryGet();
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public static final class SeServiceManager.ServiceRegisterer {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public android.os.IBinder get();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void register(@NonNull android.os.IBinder);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public android.os.IBinder tryGet();
}
}
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 8b3696a1e6d9..57e2e73854c1 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -23,6 +23,11 @@ package android.app {
method @Deprecated public android.app.Notification.Builder setTimeout(long);
}
+ public static final class Notification.WearableExtender implements android.app.Notification.Extender {
+ method @Deprecated public android.graphics.Bitmap getBackground();
+ method @Deprecated public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
+ }
+
}
package android.app.slice {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ad658061a0f6..98a454ad3eab 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -540,7 +540,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
- method @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
+ method @FlaggedApi("android.app.app_start_info") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
@@ -3193,7 +3193,7 @@ package android.companion.virtual {
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull public android.content.Context createContext();
@@ -3214,9 +3214,9 @@ package android.companion.virtual {
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
- method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
- method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
+ method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
}
@@ -3232,7 +3232,7 @@ package android.companion.virtual {
method public int getDefaultActivityPolicy();
method public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
- method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public android.content.ComponentName getHomeComponent();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
method public int getLockState();
method @Nullable public String getName();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
@@ -3247,7 +3247,7 @@ package android.companion.virtual {
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
- field @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
+ field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
@@ -3264,7 +3264,7 @@ package android.companion.virtual {
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
- method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
@@ -3805,8 +3805,8 @@ package android.content.pm {
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
- method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3817,8 +3817,8 @@ package android.content.pm {
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
- field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
- field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+ field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
@@ -3879,7 +3879,7 @@ package android.content.pm {
method public static void forceSafeLabels();
method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
- field @FlaggedApi(Flags.FLAG_ARCHIVING) public boolean isArchived;
+ field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
}
public abstract class PackageManager {
@@ -3929,7 +3929,7 @@ package android.content.pm {
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
- method @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
+ method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
@@ -3980,7 +3980,7 @@ package android.content.pm {
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 256; // 0x100
field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
- field @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) public static final int FLAG_SUSPEND_QUARANTINED = 1; // 0x1
+ field @FlaggedApi("android.content.pm.quarantined_enabled") public static final int FLAG_SUSPEND_QUARANTINED = 1; // 0x1
field public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; // 0xffffffff
field public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; // 0xfffffff3
field public static final int INSTALL_FAILED_CONTAINER_ERROR = -18; // 0xffffffee
@@ -4529,6 +4529,7 @@ package android.hardware.display {
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
+ field public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80
field public static final int VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED = 65536; // 0x10000
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
@@ -9631,67 +9632,67 @@ package android.nfc {
package android.nfc.cardemulation {
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements android.os.Parcelable {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.util.proto.ProtoOutputStream);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategory();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
- }
-
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements android.os.Parcelable {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategoryForAid(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public String getOffHostSecureElement();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getPrefixAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSettingsActivityName();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getSubsetAids();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean hasCategory(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void resetOffHostSecureElement();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setOffHostSecureElement(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
- }
-
- @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements android.os.Parcelable {
- ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getNfcid2();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSystemCode();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getT3tPmm();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicNfcid2(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicSystemCode(@NonNull String);
- method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
- field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public final class AidGroup implements android.os.Parcelable {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getCategory();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
+ }
+
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getCategoryForAid(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.content.ComponentName getComponent();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getDescription();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public String getOffHostSecureElement();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getPrefixAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSettingsActivityName();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getSubsetAids();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresScreenOn();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
+ }
+
+ @FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable {
+ ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.content.ComponentName getComponent();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getDescription();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getNfcid2();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSystemCode();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getT3tPmm();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicNfcid2(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicSystemCode(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
}
}
@@ -9842,7 +9843,6 @@ package android.os {
field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
- field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
@@ -10705,13 +10705,13 @@ package android.permission {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
- method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
- method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -16731,17 +16731,22 @@ package android.telephony.satellite {
}
public final class SatelliteManager {
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -16768,6 +16773,7 @@ package android.telephony.satellite {
field public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
field public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
field public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
@@ -16777,13 +16783,13 @@ package android.telephony.satellite {
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
- field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
- field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
field public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
field public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
- field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
field public static final int SATELLITE_MODEM_STATE_OFF = 4; // 0x4
field public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
field public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4f4569134c19..3bf2ccaf9923 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import android.Manifest;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -3982,6 +3983,7 @@ public class ActivityManager {
* the order from most recent to least recent.
*/
@NonNull
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public List<ApplicationStartInfo> getHistoricalProcessStartReasons(
@IntRange(from = 0) int maxNum) {
try {
@@ -4012,6 +4014,7 @@ public class ActivityManager {
*/
@NonNull
@SystemApi
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
@RequiresPermission(Manifest.permission.DUMP)
public List<ApplicationStartInfo> getExternalHistoricalProcessStartReasons(
@NonNull String packageName, @IntRange(from = 0) int maxNum) {
@@ -4044,6 +4047,7 @@ public class ActivityManager {
*
* @throws IllegalArgumentException if executor or listener are null.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void setApplicationStartInfoCompletionListener(@NonNull final Executor executor,
@NonNull final Consumer<ApplicationStartInfo> listener) {
Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -4065,6 +4069,7 @@ public class ActivityManager {
/**
* Removes the callback set by {@link #setApplicationStartInfoCompletionListener} if there is one.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void clearApplicationStartInfoCompletionListener() {
try {
getService().clearApplicationStartInfoCompleteListener(mContext.getUserId());
@@ -4089,6 +4094,7 @@ public class ActivityManager {
* Will thow {@link java.lang.IllegalArgumentException} if not in range.
* @param timestampNs Clock monotonic time in nanoseconds of event to be recorded.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void addStartInfoTimestamp(@IntRange(
from = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START,
to = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int key,
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 25c48e630380..183783bdf309 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,7 +62,7 @@ import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.PendingTransactionActions.StopInfo;
@@ -375,8 +375,8 @@ public final class ActivityThread extends ClientTransactionHandler
@GuardedBy("mPendingOverrideConfigs")
private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>();
/** The activities to be truly destroyed (not include relaunch). */
- final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
- Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
+ final Map<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed =
+ Collections.synchronizedMap(new ArrayMap<>());
// List of new activities that should be reported when next we idle.
final ArrayList<ActivityClientRecord> mNewActivities = new ArrayList<>();
// Number of activities that are currently visible on-screen.
@@ -5799,7 +5799,7 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed() {
+ public Map<IBinder, DestroyActivityItem> getActivitiesToBeDestroyed() {
return mActivitiesToBeDestroyed;
}
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index f5fb6edfcc04..a6a57cd5745a 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,7 @@ import java.util.Set;
/**
* Provide information related to a processes startup.
*/
+@FlaggedApi(Flags.FLAG_APP_START_INFO)
public final class ApplicationStartInfo implements Parcelable {
/**
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 98020ff2d173..25075e91088f 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -19,7 +19,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.Context;
@@ -108,7 +108,7 @@ public abstract class ClientTransactionHandler {
// and deliver callbacks.
/** Get activity and its corresponding transaction item which are going to destroy. */
- public abstract Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed();
+ public abstract Map<IBinder, DestroyActivityItem> getActivitiesToBeDestroyed();
/** Destroy the activity. */
public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java
index 1905b6a46d7e..bc6fe6146764 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -16,12 +16,15 @@
package android.app;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -31,11 +34,15 @@ import java.util.Set;
*/
@SystemService(Context.GRAMMATICAL_INFLECTION_SERVICE)
public class GrammaticalInflectionManager {
- private static final Set<Integer> VALID_GENDER_VALUES = new HashSet<>(Arrays.asList(
+
+ /** @hide */
+ @NonNull
+ public static final Set<Integer> VALID_GRAMMATICAL_GENDER_VALUES =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
Configuration.GRAMMATICAL_GENDER_NEUTRAL,
Configuration.GRAMMATICAL_GENDER_FEMININE,
- Configuration.GRAMMATICAL_GENDER_MASCULINE));
+ Configuration.GRAMMATICAL_GENDER_MASCULINE)));
private final Context mContext;
private final IGrammaticalInflectionManager mService;
@@ -79,7 +86,7 @@ public class GrammaticalInflectionManager {
*/
public void setRequestedApplicationGrammaticalGender(
@Configuration.GrammaticalGender int grammaticalGender) {
- if (!VALID_GENDER_VALUES.contains(grammaticalGender)) {
+ if (!VALID_GRAMMATICAL_GENDER_VALUES.contains(grammaticalGender)) {
throw new IllegalArgumentException("Unknown grammatical gender");
}
@@ -90,4 +97,44 @@ public class GrammaticalInflectionManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Sets the current grammatical gender for all privileged applications. The value will be
+ * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)
+ *
+ * @param grammaticalGender the terms of address the user preferred in system.
+ *
+ * @see Configuration#getGrammaticalGender
+ * @hide
+ */
+ public void setSystemWideGrammaticalGender(
+ @Configuration.GrammaticalGender int grammaticalGender) {
+ if (!VALID_GRAMMATICAL_GENDER_VALUES.contains(grammaticalGender)) {
+ throw new IllegalArgumentException("Unknown grammatical gender");
+ }
+
+ try {
+ mService.setSystemWideGrammaticalGender(mContext.getUserId(), grammaticalGender);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current grammatical gender of privileged application from the encrypted file,
+ * which is stored under {@link android.os.Environment#getDataSystemCeDirectory(int)}.
+ *
+ * @return the value of grammatical gender
+ *
+ * @see Configuration#getGrammaticalGender
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_TERMS_OF_ADDRESS_ENABLED)
+ @Configuration.GrammaticalGender
+ public int getSystemGrammaticalGender() {
+ try {
+ return mService.getSystemGrammaticalGender(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IGrammaticalInflectionManager.aidl b/core/java/android/app/IGrammaticalInflectionManager.aidl
index 9366a45551da..48a48416d592 100644
--- a/core/java/android/app/IGrammaticalInflectionManager.aidl
+++ b/core/java/android/app/IGrammaticalInflectionManager.aidl
@@ -16,4 +16,14 @@ package android.app;
* Sets a specified app’s app-specific grammatical gender.
*/
void setRequestedApplicationGrammaticalGender(String appPackageName, int userId, int gender);
- } \ No newline at end of file
+
+ /**
+ * Sets the grammatical gender to system.
+ */
+ void setSystemWideGrammaticalGender(int userId, int gender);
+
+ /**
+ * Gets the grammatical gender from system.
+ */
+ int getSystemGrammaticalGender(int userId);
+ }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7f38b27c12c8..ec5effd0963d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -98,6 +98,7 @@ interface INotificationManager
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
+ ParceledListSlice getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid);
void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
void unlockNotificationChannel(String pkg, int uid, String channelId);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c42df3c8819..93c2b5ad4d86 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -44,6 +44,9 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -296,6 +299,15 @@ public class Notification implements Parcelable
public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
/**
+ * The call to WearableExtender#setBackground(Bitmap) will have no effect and the passed
+ * Bitmap will not be retained in memory.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @VisibleForTesting
+ static final long WEARABLE_EXTENDER_BACKGROUND_BLOCKED = 270551184L;
+
+ /**
* A timestamp related to this notification, in milliseconds since the epoch.
*
* Default value: {@link System#currentTimeMillis() Now}.
@@ -11148,9 +11160,20 @@ public class Notification implements Parcelable
wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
new Notification[mPages.size()]));
}
+
if (mBackground != null) {
- wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+ // Keeping WearableExtender backgrounds in memory despite them being deprecated has
+ // added noticeable increase in system server and system ui memory usage. After
+ // target VERSION_CODE#VANILLA_ICE_CREAM the background will not be populated
+ // anymore.
+ if (CompatChanges.isChangeEnabled(WEARABLE_EXTENDER_BACKGROUND_BLOCKED)) {
+ Log.d(TAG, "Use of background in WearableExtenders has been deprecated and "
+ + "will not be populated anymore.");
+ } else {
+ wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
+ }
}
+
if (mContentIcon != 0) {
wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
}
@@ -11369,12 +11392,21 @@ public class Notification implements Parcelable
*
* @param background the background bitmap
* @return this object for method chaining
- * @see android.app.Notification.WearableExtender#getBackground
- * @deprecated Background images are no longer supported.
+ * @removed Not functional since {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}.
+ * The wearable background is not used by wearables anymore and uses up
+ * unnecessary memory.
*/
@Deprecated
public WearableExtender setBackground(Bitmap background) {
- mBackground = background;
+ // Keeping WearableExtender backgrounds in memory despite them being deprecated has
+ // added noticeable increase in system server and system ui memory usage. After
+ // target VERSION_CODE#VANILLA_ICE_CREAM the background will not be populated anymore.
+ if (CompatChanges.isChangeEnabled(WEARABLE_EXTENDER_BACKGROUND_BLOCKED)) {
+ Log.d(TAG, "Use of background in WearableExtenders has been deprecated and "
+ + "will not be populated anymore.");
+ } else {
+ mBackground = background;
+ }
return this;
}
@@ -11384,11 +11416,13 @@ public class Notification implements Parcelable
* will work with any notification style.
*
* @return the background image
- * @see android.app.Notification.WearableExtender#setBackground
- * @deprecated Background images are no longer supported.
+ * @removed Not functional since {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. The
+ * wearable background is not used by wearables anymore and uses up
+ * unnecessary memory.
*/
@Deprecated
public Bitmap getBackground() {
+ Log.w(TAG, "Use of background in WearableExtender has been removed, returning null.");
return mBackground;
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e1c45d98e678..9cf54e3b9063 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -54,6 +54,9 @@ per-file IBackupAgent.aidl = file:/services/backup/OWNERS
per-file Broadcast* = file:/BROADCASTS_OWNERS
per-file ReceiverInfo* = file:/BROADCASTS_OWNERS
+# GrammaticalInflectionManager
+per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
+
# KeyguardManager
per-file KeyguardManager.java = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 6f4abfdc05c1..6a03c17159d3 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -207,9 +207,10 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
final long identity = Binder.clearCallingIdentity();
try {
if (rotation == UiAutomation.ROTATION_UNFREEZE) {
- mWindowManager.thawRotation();
+ mWindowManager.thawRotation(/* caller= */ "UiAutomationConnection#setRotation");
} else {
- mWindowManager.freezeRotation(rotation);
+ mWindowManager.freezeRotation(rotation,
+ /* caller= */ "UiAutomationConnection#setRotation");
}
return true;
} catch (RemoteException re) {
@@ -615,11 +616,13 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
- mWindowManager.freezeRotation(mInitialFrozenRotation);
+ mWindowManager.freezeRotation(mInitialFrozenRotation,
+ /* caller= */ "UiAutomationConnection#restoreRotationStateLocked");
} else {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
- mWindowManager.thawRotation();
+ mWindowManager.thawRotation(
+ /* caller= */ "UiAutomationConnection#restoreRotationStateLocked");
}
} catch (RemoteException re) {
/* ignore */
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
new file mode 100644
index 000000000000..2076e85828a6
--- /dev/null
+++ b/core/java/android/app/activity_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ namespace: "system_performance"
+ name: "app_start_info"
+ description: "Control collecting of ApplicationStartInfo records and APIs."
+ bug: "247814855"
+} \ No newline at end of file
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ed0f872bf9bc..15bd1dcc3980 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -22,8 +22,10 @@ import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
+import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -2452,7 +2454,7 @@ public class AssistStructure implements Parcelable {
+ node.getTextStyle());
Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor())
+ ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
- Log.i(TAG, prefix + " Input type: " + node.getInputType());
+ Log.i(TAG, prefix + " Input type: " + getInputTypeString(node.getInputType()));
Log.i(TAG, prefix + " Resource id: " + node.getTextIdEntry());
}
String webDomain = node.getWebDomain();
@@ -2664,4 +2666,33 @@ public class AssistStructure implements Parcelable {
return new AssistStructure[size];
}
};
+
+ private static final ArrayMap<Integer, String> INPUT_TYPE_VARIATIONS = new ArrayMap<>();
+ static {
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, "EmailSubject");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, "PostalAddress");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PERSON_NAME, "PersonName");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PASSWORD, "Password");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, "VisiblePassword");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_URI, "URI");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, "WebEmailAddress");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, "WebPassword");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE, "LongMessage");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE, "ShortMessage");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_MULTI_LINE, "MultiLine");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE, "ImeMultiLine");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_FILTER, "Filter");
+ }
+
+ private static String getInputTypeString(int inputType) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(inputType);
+ sb.append("(class=").append(inputType & InputType.TYPE_MASK_CLASS).append(')');
+ for (int variation : INPUT_TYPE_VARIATIONS.keySet()) {
+ if ((variation & inputType) == variation) {
+ sb.append('|').append(INPUT_TYPE_VARIATIONS.get(variation));
+ }
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/app/grammatical_inflection_manager.aconfig b/core/java/android/app/grammatical_inflection_manager.aconfig
new file mode 100644
index 000000000000..989ce61337a3
--- /dev/null
+++ b/core/java/android/app/grammatical_inflection_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ name: "system_terms_of_address_enabled"
+ namespace: "grammatical_gender"
+ description: "Feature flag for System Terms of Address"
+ bug: "297798866"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index a5b0f18dad3b..8617386516af 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -23,7 +23,6 @@ import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -83,23 +82,6 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
return mActivityCallbacks;
}
- /** Get the target activity. */
- @Nullable
- @UnsupportedAppUsage
- public IBinder getActivityToken() {
- // TODO(b/260873529): remove after we allow multiple activity items in one transaction.
- if (mLifecycleStateRequest != null) {
- return mLifecycleStateRequest.getActivityToken();
- }
- for (int i = mActivityCallbacks.size() - 1; i >= 0; i--) {
- final IBinder token = mActivityCallbacks.get(i).getActivityToken();
- if (token != null) {
- return token;
- }
- }
- return null;
- }
-
/** Get the target state lifecycle request. */
@VisibleForTesting(visibility = PACKAGE)
@UnsupportedAppUsage
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index ddb6df10517c..f9cf075d6062 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -50,6 +50,13 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
}
@Override
+ public void postExecute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions) {
+ // Cleanup after execution.
+ client.getActivitiesToBeDestroyed().remove(getActivityToken());
+ }
+
+ @Override
public int getTargetState() {
return ON_DESTROY;
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 44336735254d..066f9fe84970 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -47,7 +47,6 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
-import java.util.Map;
/**
* Class that manages transaction execution in the correct order.
@@ -75,34 +74,14 @@ public class TransactionExecutor {
* either remain in the initial state, or last state needed by a callback.
*/
public void execute(@NonNull ClientTransaction transaction) {
- if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
-
- final IBinder token = transaction.getActivityToken();
- if (token != null) {
- final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
- mTransactionHandler.getActivitiesToBeDestroyed();
- final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
- if (destroyItem != null) {
- if (transaction.getLifecycleStateRequest() == destroyItem) {
- // It is going to execute the transaction that will destroy activity with the
- // token, so the corresponding to-be-destroyed record can be removed.
- activitiesToBeDestroyed.remove(token);
- }
- if (mTransactionHandler.getActivityClient(token) == null) {
- // The activity has not been created but has been requested to destroy, so all
- // transactions for the token are just like being cancelled.
- Slog.w(TAG, tId(transaction) + "Skip pre-destroyed transaction:\n"
- + transactionToString(transaction, mTransactionHandler));
- return;
- }
- }
+ if (DEBUG_RESOLVER) {
+ Slog.d(TAG, tId(transaction) + "Start resolving transaction");
+ Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
}
- if (DEBUG_RESOLVER) Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
-
executeCallbacks(transaction);
-
executeLifecycleState(transaction);
+
mPendingActions.clear();
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
@@ -135,6 +114,14 @@ public class TransactionExecutor {
final IBinder token = item.getActivityToken();
ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+ if (token != null && r == null
+ && mTransactionHandler.getActivitiesToBeDestroyed().containsKey(token)) {
+ // The activity has not been created but has been requested to destroy, so all
+ // transactions for the token are just like being cancelled.
+ Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item);
+ continue;
+ }
+
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
@@ -211,6 +198,10 @@ public class TransactionExecutor {
}
if (r == null) {
+ if (mTransactionHandler.getActivitiesToBeDestroyed().get(token) == lifecycleItem) {
+ // Always cleanup for destroy item.
+ lifecycleItem.postExecute(mTransactionHandler, mPendingActions);
+ }
// Ignore requests for non-existent client records for now.
return;
}
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index 4692f921beb2..ce883cddc952 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -44,6 +44,7 @@ public final class VirtualDevice implements Parcelable {
private final int mId;
private final @Nullable String mPersistentId;
private final @Nullable String mName;
+ private final @Nullable CharSequence mDisplayName;
/**
* Creates a new instance of {@link VirtualDevice}.
@@ -53,6 +54,18 @@ public final class VirtualDevice implements Parcelable {
*/
public VirtualDevice(@NonNull IVirtualDevice virtualDevice, int id,
@Nullable String persistentId, @Nullable String name) {
+ this(virtualDevice, id, persistentId, name, null);
+ }
+
+ /**
+ * Creates a new instance of {@link VirtualDevice}. Only to be used by the
+ * VirtualDeviceManagerService.
+ *
+ * @hide
+ */
+ public VirtualDevice(@NonNull IVirtualDevice virtualDevice, int id,
+ @Nullable String persistentId, @Nullable String name,
+ @Nullable CharSequence displayName) {
if (id <= Context.DEVICE_ID_DEFAULT) {
throw new IllegalArgumentException("VirtualDevice ID must be greater than "
+ Context.DEVICE_ID_DEFAULT);
@@ -61,6 +74,7 @@ public final class VirtualDevice implements Parcelable {
mId = id;
mPersistentId = persistentId;
mName = name;
+ mDisplayName = displayName;
}
private VirtualDevice(@NonNull Parcel parcel) {
@@ -68,6 +82,7 @@ public final class VirtualDevice implements Parcelable {
mId = parcel.readInt();
mPersistentId = parcel.readString8();
mName = parcel.readString8();
+ mDisplayName = parcel.readCharSequence();
}
/**
@@ -112,6 +127,15 @@ public final class VirtualDevice implements Parcelable {
}
/**
+ * Returns the human-readable name of the virtual device, if defined, which is suitable to be
+ * shown in UI.
+ */
+ @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS)
+ public @Nullable CharSequence getDisplayName() {
+ return mDisplayName;
+ }
+
+ /**
* Returns the IDs of all virtual displays that belong to this device, if any.
*
* <p>The actual {@link android.view.Display} objects can be obtained by passing the returned
@@ -156,6 +180,7 @@ public final class VirtualDevice implements Parcelable {
dest.writeInt(mId);
dest.writeString8(mPersistentId);
dest.writeString8(mName);
+ dest.writeCharSequence(mDisplayName);
}
@Override
@@ -165,6 +190,7 @@ public final class VirtualDevice implements Parcelable {
+ " mId=" + mId
+ " mPersistentId=" + mPersistentId
+ " mName=" + mName
+ + " mDisplayName=" + mDisplayName
+ ")";
}
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 3e96c96d8d17..d0e13cd977ef 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -34,3 +34,10 @@ flag {
description: "Enable Virtual Camera"
bug: "270352264"
}
+
+flag {
+ name: "stream_permissions"
+ namespace: "virtual_devices"
+ description: "Enable streaming permission dialogs to Virtual Devices"
+ bug: "291737919"
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index f0c87a138a98..990ebc5fdbcd 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -27,6 +27,7 @@ import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -364,11 +365,20 @@ public final class DisplayManager {
/**
* Virtual display flag: Indicates that the orientation of this display device is coupled to
- * the rotation of its associated logical display.
+ * the orientation of its associated logical display.
+ * <p>
+ * The flag should not be set when the physical display is mounted in a fixed orientation
+ * such as on a desk. Without this flag, display manager will apply a coordinate transformation
+ * such as a scale and translation to letterbox or pillarbox format under the assumption that
+ * the physical orientation of the display is invariant. With this flag set, the content will
+ * rotate to fill in the space of the display, as it does on the internal device display.
+ * </p>
*
* @see #createVirtualDisplay
* @hide
*/
+ @SuppressLint("UnflaggedApi")
+ @SystemApi
public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
/**
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4700720736b5..4791a8341912 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -698,4 +698,34 @@ public abstract class DisplayManagerInternal {
return "AmbientLightSensorData(" + sensorName + ", " + sensorType + ")";
}
}
+
+ /**
+ * Associate a internal display to a {@link DisplayOffloader}.
+ *
+ * @param displayId the id of the internal display.
+ * @param displayOffloader the {@link DisplayOffloader} that controls offloading ops of internal
+ * display whose id is displayId.
+ * @return a {@link DisplayOffloadSession} associated with given displayId and displayOffloader.
+ */
+ public abstract DisplayOffloadSession registerDisplayOffloader(
+ int displayId, DisplayOffloader displayOffloader);
+
+ /** The callbacks that controls the entry & exit of display offloading. */
+ public interface DisplayOffloader {
+ boolean startOffload();
+
+ void stopOffload();
+ }
+
+ /** A session token that associates a internal display with a {@link DisplayOffloader}. */
+ public interface DisplayOffloadSession {
+ /** Provide the display state to use in place of state DOZE. */
+ void setDozeStateOverride(int displayState);
+ /** Returns the associated DisplayOffloader. */
+ DisplayOffloader getDisplayOffloader();
+ /** Returns whether displayoffload supports the given display state. */
+ static boolean isSupportedOffloadState(int displayState) {
+ return Display.isSuspendedState(displayState);
+ }
+ }
}
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 727716e67f28..12442ba12044 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -147,8 +147,8 @@ public final class ProgramSelector implements Parcelable {
*
* <p>Consists of (from the LSB):
* <li>
- * <ul>132bit: Station ID number.
- * <ul>14bit: HD_SUBCHANNEL.
+ * <ul>32bit: Station ID number.
+ * <ul>4bit: HD_SUBCHANNEL.
* <ul>18bit: AMFM_FREQUENCY.
* </li>
*
diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig
new file mode 100644
index 000000000000..dbc1a4b21cfb
--- /dev/null
+++ b/core/java/android/hardware/radio/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.hardware.radio"
+
+flag {
+ name: "hd_radio_improved"
+ namespace: "car_framework"
+ description: "Feature flag for improved HD radio support with less vendor extensions"
+ bug: "280300929"
+}
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index af9a73ca31ee..53a3ea516530 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -29,7 +29,7 @@ public class WakeupEvent {
public String iface;
public int uid;
public int ethertype;
- public MacAddress dstHwAddr;
+ public MacAddress dstHwAddr; // actually used to store a src mac address
public String srcIp;
public String dstIp;
public int ipNextHeader;
@@ -44,7 +44,7 @@ public class WakeupEvent {
j.add(iface);
j.add("uid: " + Integer.toString(uid));
j.add("eth=0x" + Integer.toHexString(ethertype));
- j.add("dstHw=" + dstHwAddr);
+ j.add("srcMac=" + dstHwAddr); // really!! http://b/292404319#comment11
if (ipNextHeader > 0) {
j.add("ipNxtHdr=" + ipNextHeader);
j.add("srcIp=" + srcIp);
diff --git a/core/java/android/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md
index 04c658c39ad3..20f3d747cf0c 100644
--- a/core/java/android/net/network-policy-restrictions.md
+++ b/core/java/android/net/network-policy-restrictions.md
@@ -29,8 +29,8 @@ More specifically:
| **DS** | *AL* | ok | blk | ok | ok |
| **ON** | *!AL* | blk | blk | blk | blk |
| | *DL* | blk | blk | blk | blk |
-| **DS** | *AL* | blk | blk | ok | ok |
-| **OFF** | *!AL* | blk | blk | ok | ok |
+| **DS** | *AL* | ok | blk | ok | ok |
+| **OFF** | *!AL* | ok | blk | ok | ok |
| | *DL* | blk | blk | blk | blk |
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index ada55325aded..f817fb8dcaed 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -32,7 +32,9 @@ import java.io.FileDescriptor;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -613,15 +615,35 @@ public final class BinderProxy implements IBinder {
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
+
+ /* This list is to hold strong reference to the death recipients that are waiting for the death
+ * of binder that this proxy references. Previously, the death recipients were strongy
+ * referenced from JNI, but that can cause memory leak (b/298374304) when the application has a
+ * strong reference from the death recipient to the proxy object. The JNI reference is now weak.
+ * And this strong reference is to keep death recipients at least until the proxy is GC'ed. */
+ private List<DeathRecipient> mDeathRecipients = Collections.synchronizedList(new ArrayList<>());
+
/**
* See {@link IBinder#linkToDeath(DeathRecipient, int)}
*/
- public native void linkToDeath(DeathRecipient recipient, int flags)
- throws RemoteException;
+ public void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException {
+ linkToDeathNative(recipient, flags);
+ mDeathRecipients.add(recipient);
+ }
+
/**
* See {@link IBinder#unlinkToDeath}
*/
- public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ mDeathRecipients.remove(recipient);
+ return unlinkToDeathNative(recipient, flags);
+ }
+
+ private native void linkToDeathNative(DeathRecipient recipient, int flags)
+ throws RemoteException;
+
+ private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
/**
* Perform a dump on the remote object
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index f10467f0760e..47ad72fe8d0e 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -124,6 +124,8 @@ public final class BugreportParams {
/**
* Options for a lightweight bugreport intended to be taken for onboarding-related flows.
+ *
+ * @hide
*/
public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 80b7d40c14c5..4c8ef97a7437 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2940,6 +2940,12 @@ public class UserManager {
* Used to check if the context user is a restricted profile. Restricted profiles
* may have a reduced number of available apps, app restrictions, and account restrictions.
*
+ * <p>The caller must be in the same profile group as the context user or else hold
+ * <li>{@link android.Manifest.permission#MANAGE_USERS},
+ * <li>or {@link android.Manifest.permission#CREATE_USERS},
+ * <li>or, for devices after {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link android.Manifest.permission#QUERY_USERS}.
+ *
* @return whether the context user is a restricted profile.
* @hide
*/
@@ -2963,6 +2969,12 @@ public class UserManager {
* Check if a user is a restricted profile. Restricted profiles may have a reduced number of
* available apps, app restrictions, and account restrictions.
*
+ * <p>Requires
+ * <li>{@link android.Manifest.permission#MANAGE_USERS},
+ * <li>or {@link android.Manifest.permission#CREATE_USERS},
+ * <li>or, for devices after {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link android.Manifest.permission#QUERY_USERS}.
+ *
* @param user the user to check
* @return whether the user is a restricted profile.
* @hide
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 77be5d400853..1294f983b3b9 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -6,3 +6,10 @@ flag {
description: "enable device aware permission APIs"
bug: "274852670"
}
+
+flag {
+ name: "voice_activation_permission_apis"
+ namespace: "permissions"
+ description: "enable voice activation permission APIs"
+ bug: "287264308"
+} \ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e829ca288925..f40232b266d0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9908,6 +9908,48 @@ public final class Settings {
*/
public static final String DOCK_SETUP_STATE = "dock_setup_state";
+
+ /**
+ * Default, indicates that the user has not yet started the hub mode tutorial.
+ *
+ * @hide
+ */
+ public static final int HUB_MODE_TUTORIAL_NOT_STARTED = 0;
+
+ /**
+ * Indicates that the user has started but not yet completed the hub mode tutorial.
+ * One of the possible states for {@link #HUB_MODE_TUTORIAL_STATE}.
+ *
+ * @hide
+ */
+ public static final int HUB_MODE_TUTORIAL_STARTED = 1;
+
+ /**
+ * Indicates that the user has completed the hub mode tutorial.
+ * One of the possible states for {@link #HUB_MODE_TUTORIAL_STATE}.
+ *
+ * @hide
+ */
+ public static final int HUB_MODE_TUTORIAL_COMPLETED = 10;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ HUB_MODE_TUTORIAL_NOT_STARTED,
+ HUB_MODE_TUTORIAL_STARTED,
+ HUB_MODE_TUTORIAL_COMPLETED
+ })
+ public @interface HubModeTutorialState {
+ }
+
+ /**
+ * Defines the user's current state of navigating through the hub mode tutorial.
+ * The possible states are defined in {@link HubModeTutorialState}.
+ *
+ * @hide
+ */
+ public static final String HUB_MODE_TUTORIAL_STATE = "hub_mode_tutorial_state";
+
/**
* The default NFC payment component
* @hide
@@ -11105,6 +11147,12 @@ public final class Settings {
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
+ * Volume dialog timeout in ms.
+ * @hide
+ */
+ public static final String VOLUME_DIALOG_DISMISS_TIMEOUT = "volume_dialog_dismiss_timeout";
+
+ /**
* What behavior should be invoked when the volume hush gesture is triggered
* One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
*
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 2dbb5da9a258..dae3202b2043 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -76,28 +76,38 @@ public final class FileIntegrityManager {
* Enables fs-verity to the owned file under the calling app's private directory. It always uses
* the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt.
*
- * The operation can only succeed when the file is not opened as writable by any process.
+ * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be
+ * writable by the app and not already have fs-verity enabled, and the file must not currently
+ * be open for writing by any process. To check whether the device supports fs-verity, use
+ * {@link #isApkVeritySupported()}.
*
- * It takes O(file size) time to build the underlying data structure for continuous
+ * <p>It takes O(file size) time to build the underlying data structure for continuous
* verification. The operation is atomic, i.e. it's either enabled or not, even in case of
* power failure during or after the call.
*
- * Note for the API users: When the file's authenticity is crucial, the app typical needs to
+ * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to
* perform a signature check by itself before using the file. The signature is often delivered
* as a separate file and stored next to the targeting file in the filesystem. The public key of
* the signer (normally the same app developer) can be put in the APK, and the app can use the
* public key to verify the signature to the file's actual fs-verity digest (from {@link
- * #getFsVerityDigest}) before using the file. The exact format is not prescribed by the
+ * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the
* framework. App developers may choose to use common practices like JCA for the signing and
* verification, or their own preferred approach.
*
- * @param file The file to enable fs-verity. It should be an absolute path.
+ * @param file The file to enable fs-verity. It must represent an absolute path.
+ * @throws IllegalArgumentException If the provided file is not an absolute path.
+ * @throws IOException If the operation failed.
*
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
public void setupFsVerity(@NonNull File file) throws IOException {
if (!file.isAbsolute()) {
+ // fs-verity is to be enabled by installd, which enforces the validation to the
+ // (untrusted) file path passed from here. To make this less error prone, installd
+ // accepts only absolute path. When a relative path is provided, we fail with an
+ // explicit exception to help developers understand the requirement to use an absolute
+ // path.
throw new IllegalArgumentException("Expect an absolute path");
}
IFsveritySetupAuthToken authToken;
@@ -121,11 +131,12 @@ public final class FileIntegrityManager {
}
/**
- * Returns the fs-verity digest for the owned file under the calling app's
- * private directory, or null when the file does not have fs-verity enabled.
+ * Returns the fs-verity digest for the owned file under the calling app's private directory, or
+ * null when the file does not have fs-verity enabled (including when fs-verity is not supported
+ * on older devices).
*
* @param file The file to measure the fs-verity digest.
- * @return The fs-verity digeset in byte[], null if none.
+ * @return The fs-verity digest in byte[], null if none.
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 94d851603064..6a82f6da67b3 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -959,8 +959,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
}
- notifyStateChangedLocked();
}
+ notifyStateChanged(availability);
}
/**
@@ -1370,8 +1370,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mAvailability = STATE_INVALID;
mIsAvailabilityOverriddenByTestApi = false;
- notifyStateChangedLocked();
}
+ notifyStateChanged(STATE_INVALID);
super.destroy();
}
@@ -1401,6 +1401,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
*/
// TODO(b/281608561): remove the enrollment flow from AlwaysOnHotwordDetector
void onSoundModelsChanged() {
+ boolean notifyError = false;
+
synchronized (mLock) {
if (mAvailability == STATE_INVALID
|| mAvailability == STATE_HARDWARE_UNAVAILABLE
@@ -1441,6 +1443,9 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
// calling stopRecognition where there is no started session.
Log.w(TAG, "Failed to stop recognition after enrollment update: code="
+ result);
+
+ // Execute a refresh availability task - which should then notify of a change.
+ new RefreshAvailabilityTask().execute();
} catch (Exception e) {
Slog.w(TAG, "Failed to stop recognition after enrollment update", e);
if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
@@ -1449,14 +1454,14 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
+ Log.getStackTraceString(e),
FailureSuggestedAction.RECREATE_DETECTOR));
} else {
- updateAndNotifyStateChangedLocked(STATE_ERROR);
+ notifyError = true;
}
- return;
}
}
+ }
- // Execute a refresh availability task - which should then notify of a change.
- new RefreshAvailabilityTask().execute();
+ if (notifyError) {
+ updateAndNotifyStateChanged(STATE_ERROR);
}
}
@@ -1572,10 +1577,11 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
}
- @GuardedBy("mLock")
- private void updateAndNotifyStateChangedLocked(int availability) {
- updateAvailabilityLocked(availability);
- notifyStateChangedLocked();
+ private void updateAndNotifyStateChanged(int availability) {
+ synchronized (mLock) {
+ updateAvailabilityLocked(availability);
+ }
+ notifyStateChanged(availability);
}
@GuardedBy("mLock")
@@ -1589,17 +1595,17 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
}
- @GuardedBy("mLock")
- private void notifyStateChangedLocked() {
+ private void notifyStateChanged(int newAvailability) {
Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
- message.arg1 = mAvailability;
+ message.arg1 = newAvailability;
message.sendToTarget();
}
- @GuardedBy("mLock")
private void sendUnknownFailure(String failureMessage) {
- // update but do not call onAvailabilityChanged callback for STATE_ERROR
- updateAvailabilityLocked(STATE_ERROR);
+ synchronized (mLock) {
+ // update but do not call onAvailabilityChanged callback for STATE_ERROR
+ updateAvailabilityLocked(STATE_ERROR);
+ }
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, failureMessage).sendToTarget();
}
@@ -1802,19 +1808,17 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
availability = STATE_KEYPHRASE_UNENROLLED;
}
}
- updateAndNotifyStateChangedLocked(availability);
}
+ updateAndNotifyStateChanged(availability);
} catch (Exception e) {
// Any exception here not caught will crash the process because AsyncTask does not
// bubble up the exceptions to the client app, so we must propagate it to the app.
Slog.w(TAG, "Failed to refresh availability", e);
- synchronized (mLock) {
- if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
- sendUnknownFailure(
- "Failed to refresh availability: " + Log.getStackTraceString(e));
- } else {
- updateAndNotifyStateChangedLocked(STATE_ERROR);
- }
+ if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
+ sendUnknownFailure(
+ "Failed to refresh availability: " + Log.getStackTraceString(e));
+ } else {
+ updateAndNotifyStateChanged(STATE_ERROR);
}
}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
new file mode 100644
index 000000000000..46fa5017106b
--- /dev/null
+++ b/core/java/android/text/ClientFlags.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import com.android.text.flags.Flags;
+
+/**
+ * An aconfig feature flags that can be accessible from application process without
+ * ContentProvider IPCs.
+ *
+ * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
+ *
+ * @hide
+ */
+public class ClientFlags {
+
+ /**
+ * @see Flags#deprecateFontsXml()
+ */
+ public static boolean deprecateFontsXml() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_DEPRECATE_FONTS_XML);
+ }
+
+ /**
+ * @see Flags#noBreakNoHyphenationSpan()
+ */
+ public static boolean noBreakNoHyphenationSpan() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN);
+ }
+
+ /**
+ * @see Flags#phraseStrictFallback()
+ */
+ public static boolean phraseStrictFallback() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_PHRASE_STRICT_FALLBACK);
+ }
+
+ /**
+ * @see Flags#useBoundsForWidth()
+ */
+ public static boolean useBoundsForWidth() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
+ }
+}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 4be6a8def852..536e3ccd98f6 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -16,6 +16,11 @@
package android.text;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+
+import com.android.text.flags.Flags;
+
/**
* Flags in the "text" namespace.
*
@@ -46,4 +51,28 @@ public final class TextFlags {
*/
public static final boolean ENABLE_NEW_CONTEXT_MENU_DEFAULT = true;
+ /**
+ * List of text flags to be transferred to the application process.
+ */
+ public static final String[] TEXT_ACONFIGS_FLAGS = {
+ Flags.FLAG_DEPRECATE_FONTS_XML,
+ Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
+ Flags.FLAG_PHRASE_STRICT_FALLBACK,
+ Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
+ };
+
+ /**
+ * Get a key for the feature flag.
+ */
+ public static String getKeyForFlag(@NonNull String flag) {
+ return "text__" + flag;
+ }
+
+ /**
+ * Return true if the feature flag is enabled.
+ */
+ public static boolean isFeatureEnabled(@NonNull String flag) {
+ return AppGlobals.getIntCoreSetting(
+ getKeyForFlag(flag), 0 /* aconfig is false by default */) != 0;
+ }
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index a46136a53e95..31d759ea92e6 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -264,6 +264,16 @@ public abstract class DisplayEventReceiver {
}
/**
+ * Called when a display hotplug event with connection error is received.
+ *
+ * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+ * timebase.
+ * @param connectionError the hotplug connection error code.
+ */
+ public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+ }
+
+ /**
* Called when a display mode changed event is received.
*
* @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
@@ -345,6 +355,11 @@ public abstract class DisplayEventReceiver {
onHotplug(timestampNanos, physicalDisplayId, connected);
}
+ @SuppressWarnings("unused")
+ private void dispatchHotplugConnectionError(long timestampNanos, int connectionError) {
+ onHotplugConnectionError(timestampNanos, connectionError);
+ }
+
// Called from native code.
@SuppressWarnings("unused")
private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 512f4f2b5d22..981911ec8880 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.Display.Mode.INVALID_MODE_ID;
import static android.view.DisplayInfoProto.APP_HEIGHT;
import static android.view.DisplayInfoProto.APP_WIDTH;
import static android.view.DisplayInfoProto.CUTOUT;
@@ -200,6 +201,11 @@ public final class DisplayInfo implements Parcelable {
public int defaultModeId;
/**
+ * The user preferred display mode.
+ */
+ public int userPreferredModeId = INVALID_MODE_ID;
+
+ /**
* The supported modes of this display.
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
@@ -420,6 +426,7 @@ public final class DisplayInfo implements Parcelable {
&& modeId == other.modeId
&& renderFrameRate == other.renderFrameRate
&& defaultModeId == other.defaultModeId
+ && userPreferredModeId == other.userPreferredModeId
&& Arrays.equals(supportedModes, other.supportedModes)
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
@@ -478,6 +485,7 @@ public final class DisplayInfo implements Parcelable {
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
defaultModeId = other.defaultModeId;
+ userPreferredModeId = other.userPreferredModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
colorMode = other.colorMode;
supportedColorModes = Arrays.copyOf(
@@ -530,6 +538,7 @@ public final class DisplayInfo implements Parcelable {
modeId = source.readInt();
renderFrameRate = source.readFloat();
defaultModeId = source.readInt();
+ userPreferredModeId = source.readInt();
int nModes = source.readInt();
supportedModes = new Display.Mode[nModes];
for (int i = 0; i < nModes; i++) {
@@ -596,6 +605,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(modeId);
dest.writeFloat(renderFrameRate);
dest.writeInt(defaultModeId);
+ dest.writeInt(userPreferredModeId);
dest.writeInt(supportedModes.length);
for (int i = 0; i < supportedModes.length; i++) {
supportedModes[i].writeToParcel(dest, flags);
@@ -832,9 +842,12 @@ public final class DisplayInfo implements Parcelable {
sb.append(presentationDeadlineNanos);
sb.append(", mode ");
sb.append(modeId);
+ sb.append(", renderFrameRate ");
sb.append(renderFrameRate);
sb.append(", defaultMode ");
sb.append(defaultModeId);
+ sb.append(", userPreferredModeId ");
+ sb.append(userPreferredModeId);
sb.append(", modes ");
sb.append(Arrays.toString(supportedModes));
sb.append(", hdrCapabilities ");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d3b7a5be47ba..cccac95b9caa 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -316,14 +316,14 @@ interface IWindowManager
* android.view.Display#DEFAULT_DISPLAY} and given rotation.
*/
@UnsupportedAppUsage
- void freezeRotation(int rotation);
+ void freezeRotation(int rotation, String caller);
/**
* Equivalent to calling {@link #thawDisplayRotation(int)} with {@link
* android.view.Display#DEFAULT_DISPLAY}.
*/
@UnsupportedAppUsage
- void thawRotation();
+ void thawRotation(String caller);
/**
* Equivelant to call {@link #isDisplayRotationFrozen(int)} with {@link
@@ -341,7 +341,7 @@ interface IWindowManager
* {@link android.view.Surface#ROTATION_270} or -1 to freeze it to current rotation.
* @hide
*/
- void freezeDisplayRotation(int displayId, int rotation);
+ void freezeDisplayRotation(int displayId, int rotation, String caller);
/**
* Release the orientation lock imposed by freezeRotation() on the display.
@@ -349,7 +349,7 @@ interface IWindowManager
* @param displayId the ID of display which rotation should be thawed.
* @hide
*/
- void thawDisplayRotation(int displayId);
+ void thawDisplayRotation(int displayId, String caller);
/**
* Gets whether the rotation is frozen on the display.
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index bdd0a9cf653a..e4e8b7b5b6a1 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -30,8 +31,10 @@ import android.graphics.SurfaceTexture;
import android.graphics.TextureLayer;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.flags.Flags;
/**
* <p>A TextureView can be used to display a content stream, such as that
@@ -51,9 +54,9 @@ import android.util.Log;
* <th style="text-align: center;">SurfaceView</th>
* </tr>
* <tr>
- * <td>Supports alpha</td>
+ * <td>Supports View alpha</td>
* <td style="text-align: center;">X</td>
- * <td style="text-align: center;">&nbsp;</td>
+ * <td style="text-align: center;">U+</td>
* </tr>
* <tr>
* <td>Supports rotations</td>
@@ -194,6 +197,9 @@ public class TextureView extends View {
private Canvas mCanvas;
private int mSaveCount;
+ @FloatRange(from = 0.0) float mFrameRate;
+ @Surface.FrameRateCompatibility int mFrameRateCompatibility;
+
private final Object[] mNativeWindowLock = new Object[0];
// Set by native code, do not write!
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -465,6 +471,16 @@ public class TextureView extends View {
mLayer.setSurfaceTexture(mSurface);
mSurface.setDefaultBufferSize(getWidth(), getHeight());
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+ if (Flags.toolkitSetFrameRate()) {
+ mSurface.setOnSetFrameRateListener(
+ (surfaceTexture, frameRate, compatibility, strategy) -> {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.instant(Trace.TRACE_TAG_VIEW, "setFrameRate: " + frameRate);
+ }
+ mFrameRate = frameRate;
+ mFrameRateCompatibility = compatibility;
+ }, mAttachInfo.mHandler);
+ }
if (mListener != null && createNewSurface) {
mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 55374b994cd4..5eaded218a0b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8562,6 +8562,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* announcements every time a View is updated.
*
* <p>
+ * For notifying users about errors, such as in a login screen with text that displays an
+ * "incorrect password" notification, that view should send an AccessibilityEvent of type
+ * {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set
+ * {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose
+ * error-setting methods that support accessibility automatically. For example, instead of
+ * explicitly sending this event when using a TextView, use
+ * {@link android.widget.TextView#setError(CharSequence)}.
+ *
+ * <p>
* Use {@link #setStateDescription(CharSequence)} to convey state changes to views within the
* user interface. While a live region may send different types of events generated by the view,
* state description will send {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events of
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0ba5d06b64dc..f4213510a1c1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3858,9 +3858,7 @@ public final class ViewRootImpl implements ViewParent,
mPendingTransitions.clear();
}
- if (mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
- }
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
} else if (cancelAndRedraw) {
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
@@ -3874,8 +3872,8 @@ public final class ViewRootImpl implements ViewParent,
}
mPendingTransitions.clear();
}
- if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
+ if (!performDraw(mActiveSurfaceSyncGroup)) {
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
}
}
@@ -3890,6 +3888,7 @@ public final class ViewRootImpl implements ViewParent,
mReportNextDraw = false;
mLastReportNextDrawReason = null;
mActiveSurfaceSyncGroup = null;
+ mHasPendingTransactions = false;
mSyncBuffer = false;
if (isInWMSRequestedSync()) {
mWmsRequestSyncGroup.markSyncReady();
@@ -4688,7 +4687,8 @@ public final class ViewRootImpl implements ViewParent,
return false;
}
- final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
+ final boolean fullRedrawNeeded =
+ mFullRedrawNeeded || surfaceSyncGroup != null || mHasPendingTransactions;
mFullRedrawNeeded = false;
mIsDrawing = true;
@@ -4718,8 +4718,15 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
- if (mReportNextDraw) {
+ final Transaction pendingTransaction;
+ if (!usingAsyncReport && mHasPendingTransactions) {
+ pendingTransaction = new Transaction();
+ pendingTransaction.merge(mPendingTransaction);
+ } else {
+ pendingTransaction = null;
+ }
+ if (mReportNextDraw) {
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
try {
@@ -4741,9 +4748,7 @@ public final class ViewRootImpl implements ViewParent,
if (mSurfaceHolder != null && mSurface.isValid()) {
usingAsyncReport = true;
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
- if (surfaceSyncGroup != null) {
- surfaceSyncGroup.markSyncReady();
- }
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
});
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -4756,15 +4761,27 @@ public final class ViewRootImpl implements ViewParent,
}
}
- if (surfaceSyncGroup != null && !usingAsyncReport) {
- surfaceSyncGroup.markSyncReady();
+ if (!usingAsyncReport) {
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
}
+
if (mPerformContentCapture) {
performContentCaptureInitialReport();
}
return true;
}
+ private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup,
+ @Nullable Transaction pendingTransaction) {
+ if (surfaceSyncGroup != null) {
+ if (pendingTransaction != null) {
+ surfaceSyncGroup.addTransaction(pendingTransaction);
+ }
+ surfaceSyncGroup.markSyncReady();
+ } else if (pendingTransaction != null) {
+ pendingTransaction.apply();
+ }
+ }
/**
* Checks (and caches) if content capture is enabled for this context.
*/
@@ -4850,8 +4867,8 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private boolean draw(boolean fullRedrawNeeded,
- @Nullable SurfaceSyncGroup activeSyncGroup, boolean syncBuffer) {
+ private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup,
+ boolean syncBuffer) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
@@ -4995,12 +5012,11 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mThreadedRenderer.forceDrawNextFrame();
}
} else if (mHasPendingTransactions) {
- // Register a calback if there's no sync involved but there were calls to
+ // Register a callback if there's no sync involved but there were calls to
// applyTransactionOnDraw. If there is a sync involved, the sync callback will
// handle merging the pending transaction.
registerCallbackForPendingTransactions();
}
- mHasPendingTransactions = false;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
@@ -8977,13 +8993,7 @@ public final class ViewRootImpl implements ViewParent,
mAdded = false;
AnimationHandler.removeRequestor(this);
}
- if (mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
- mActiveSurfaceSyncGroup = null;
- }
- if (mHasPendingTransactions) {
- mPendingTransaction.apply();
- }
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -11502,9 +11512,7 @@ public final class ViewRootImpl implements ViewParent,
Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
}
- Transaction t = new Transaction();
- t.merge(mPendingTransaction);
-
+ surfaceSyncGroup.addTransaction(mPendingTransaction);
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
@@ -11518,7 +11526,6 @@ public final class ViewRootImpl implements ViewParent,
+ frame + ".");
}
- mergeWithNextTransaction(t, frame);
// If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
// SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
// any blast sync or commit callback, and the code should directly call
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a0d0656a4e50..2c413300ef17 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -103,6 +103,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.BoringLayout;
+import android.text.ClientFlags;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
@@ -1634,7 +1635,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = false; // TODO: Connect to the flag.
+ mUseBoundsForWidth = ClientFlags.useBoundsForWidth();
} else {
mUseBoundsForWidth = false;
}
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index a88e394c0985..451acbe84a60 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -122,7 +122,7 @@ public final class StartingWindowInfo implements Parcelable {
TYPE_PARAMETER_PROCESS_RUNNING,
TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
TYPE_PARAMETER_ACTIVITY_CREATED,
- TYPE_PARAMETER_ALLOW_ICON,
+ TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
TYPE_PARAMETER_WINDOWLESS,
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
@@ -143,7 +143,7 @@ public final class StartingWindowInfo implements Parcelable {
/** @hide */
public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
/** @hide */
- public static final int TYPE_PARAMETER_ALLOW_ICON = 0x00000020;
+ public static final int TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN = 0x00000020;
/**
* The parameter which indicates if the activity has finished drawing.
* @hide
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 9b10a7ff5d12..932608a3b57b 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.view.WindowManager.transitTypeToString;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -88,6 +90,11 @@ public final class TransitionRequestInfo implements Parcelable {
this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
}
+ /** @hide */
+ String typeToString() {
+ return transitTypeToString(mType);
+ }
+
/** Requested change to a display. */
@DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
public static class DisplayChange implements Parcelable {
@@ -263,7 +270,7 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1693425051905L,
+ time = 1695667226050L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -298,11 +305,11 @@ public final class TransitionRequestInfo implements Parcelable {
* @param type
* The type of the transition being requested.
* @param triggerTask
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
* @param pipTask
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
- * finish) has caused this transition to occur.
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -431,7 +438,7 @@ public final class TransitionRequestInfo implements Parcelable {
// String fieldNameToString() { ... }
return "TransitionRequestInfo { " +
- "type = " + mType + ", " +
+ "type = " + typeToString() + ", " +
"triggerTask = " + mTriggerTask + ", " +
"pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
@@ -506,10 +513,10 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1693425051928L,
+ time = 1695667226088L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 0d1871d379fa..663067ce7ae6 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -203,14 +203,22 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
}
return false;
}
-
+
+ /**
+ * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
+ * Intent.ACTION_PACKAGE_CHANGED} being received, this callback
+ * has extras passed in.
+ */
+ public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+ }
+
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
return false;
}
public void onHandleUserStop(Intent intent, int userHandle) {
}
-
+
public void onUidRemoved(int uid) {
}
@@ -238,21 +246,34 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
}
/**
+ * Called when a package disappears with extras passed in.
+ */
+ public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+ }
+
+ /**
* Called when a package appears for any reason.
*/
public void onPackageAppeared(String packageName, int reason) {
}
+
+ /**
+ * Called when a package appears with extras passed in.
+ */
+ public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+ }
+
/**
* Called when an existing package is updated or its disabled state changes.
*/
public void onPackageModified(@NonNull String packageName) {
}
-
+
public boolean didSomePackagesChange() {
return mSomePackagesChanged;
}
-
+
public int isPackageAppearing(String packageName) {
if (mAppearingPackages != null) {
for (int i=mAppearingPackages.length-1; i>=0; i--) {
@@ -381,6 +402,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
mChangeType = PACKAGE_PERMANENT_CHANGE;
onPackageAdded(pkg, uid);
}
+ onPackageAppearedWithExtras(pkg, intent.getExtras());
onPackageAppeared(pkg, mChangeType);
}
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
@@ -403,6 +425,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
onPackageRemovedAllUsers(pkg, uid);
}
}
+ onPackageDisappearedWithExtras(pkg, intent.getExtras());
onPackageDisappeared(pkg, mChangeType);
}
} else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -417,6 +440,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
if (onPackageChanged(pkg, uid, mModifiedComponents)) {
mSomePackagesChanged = true;
}
+ onPackageChangedWithExtras(pkg, intent.getExtras());
onPackageModified(pkg);
}
} else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index 71f997bb57c5..71576837c2e1 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -1,4 +1,4 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
jsharkey@android.com
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c6f5086b8346..7eeac29e21f7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -154,6 +154,7 @@ oneway interface IStatusBar
void addQsTile(in ComponentName tile);
void remQsTile(in ComponentName tile);
+ void setQsTiles(in String[] tiles);
void clickQsTile(in ComponentName tile);
void handleSystemKey(in KeyEvent key);
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 058c6ec4d13c..6e45796df053 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -105,23 +105,23 @@ public final class RotationPolicy {
/**
* Enables or disables rotation lock from the system UI toggle.
*/
- public static void setRotationLock(Context context, final boolean enabled) {
+ public static void setRotationLock(Context context, final boolean enabled, String caller) {
final int rotation = areAllRotationsAllowed(context)
|| useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION
: NATURAL_ROTATION;
- setRotationLockAtAngle(context, enabled, rotation);
+ setRotationLockAtAngle(context, enabled, rotation, caller);
}
/**
* Enables or disables rotation lock at a specific rotation from system UI.
*/
public static void setRotationLockAtAngle(Context context, final boolean enabled,
- final int rotation) {
+ final int rotation, String caller) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT);
- setRotationLock(enabled, rotation);
+ setRotationLock(enabled, rotation, caller);
}
/**
@@ -129,12 +129,13 @@ public final class RotationPolicy {
*
* If rotation is locked for accessibility, the system UI toggle is hidden to avoid confusion.
*/
- public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
+ public static void setRotationLockForAccessibility(Context context, final boolean enabled,
+ String caller) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
- setRotationLock(enabled, NATURAL_ROTATION);
+ setRotationLock(enabled, NATURAL_ROTATION, caller);
}
private static boolean areAllRotationsAllowed(Context context) {
@@ -146,16 +147,17 @@ public final class RotationPolicy {
R.bool.config_useCurrentRotationOnRotationLockChange);
}
- private static void setRotationLock(final boolean enabled, final int rotation) {
+ private static void setRotationLock(final boolean enabled, final int rotation,
+ final String caller) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (enabled) {
- wm.freezeRotation(rotation);
+ wm.freezeRotation(rotation, caller);
} else {
- wm.thawRotation();
+ wm.thawRotation(caller);
}
} catch (RemoteException exc) {
Log.w(TAG, "Unable to save auto-rotate setting");
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index acb0e44f29a6..89f46599322e 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -49,6 +49,7 @@ public class CallLayout extends FrameLayout {
private CachingIconView mIcon;
private CachingIconView mConversationIconBadgeBg;
private TextView mConversationText;
+ private boolean mSetDataAsyncEnabled = false;
public CallLayout(@NonNull Context context) {
super(context);
@@ -83,7 +84,8 @@ public class CallLayout extends FrameLayout {
});
}
- private void updateCallLayout() {
+ @NonNull
+ private Icon getConversationIcon() {
CharSequence callerName = "";
String symbol = "";
Icon icon = null;
@@ -98,8 +100,7 @@ public class CallLayout extends FrameLayout {
if (icon == null) {
icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor);
}
- // TODO(b/179178086): crop/clip the icon to a circle?
- mConversationIconView.setImageIcon(icon);
+ return icon;
}
@RemotableViewMethod
@@ -123,10 +124,38 @@ public class CallLayout extends FrameLayout {
/**
* Set the notification extras so that this layout has access
*/
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setDataAsync")
public void setData(Bundle extras) {
- setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON, android.app.Person.class));
- updateCallLayout();
+ final Person person = getPerson(extras);
+ setUser(person);
+
+ final Icon icon = getConversationIcon();
+ mConversationIconView.setImageIcon(icon);
+ }
+
+
+ public void setSetDataAsyncEnabled(boolean setDataAsyncEnabled) {
+ mSetDataAsyncEnabled = setDataAsyncEnabled;
+ }
+
+ /**
+ * Async implementation for setData
+ */
+ public Runnable setDataAsync(Bundle extras) {
+ if (!mSetDataAsyncEnabled) {
+ return () -> setData(extras);
+ }
+
+ final Person person = getPerson(extras);
+ setUser(person);
+
+ final Icon conversationIcon = getConversationIcon();
+ return mConversationIconView.setImageIconAsync(conversationIcon);
+ }
+
+ @Nullable
+ private Person getPerson(Bundle extras) {
+ return extras.getParcelable(Notification.EXTRA_CALL_PERSON, Person.class);
}
private void setUser(Person user) {
diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS
index 62c5737a2e8e..c24680e9b06a 100644
--- a/core/java/com/android/server/net/OWNERS
+++ b/core/java/com/android/server/net/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index ad196c014dec..3795fc8594ae 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -15,7 +15,19 @@ license {
],
}
-cc_library_shared {
+soong_config_module_type {
+ name: "cc_library_shared_for_libandroid_runtime",
+ module_type: "cc_library_shared",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_binder_death_recipient_weak_from_jni",
+ ],
+ properties: [
+ "cflags",
+ ],
+}
+
+cc_library_shared_for_libandroid_runtime {
name: "libandroid_runtime",
host_supported: true,
cflags: [
@@ -46,6 +58,12 @@ cc_library_shared {
},
},
+ soong_config_variables: {
+ release_binder_death_recipient_weak_from_jni: {
+ cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
+ },
+ },
+
cpp_std: "gnu++20",
srcs: [
@@ -84,6 +102,7 @@ cc_library_shared {
static_libs: [
"libnativehelper_lazy",
"libziparchive_for_incfs",
+ "libguiflags",
],
export_include_dirs: [
diff --git a/core/jni/android_graphics_SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp
index 21487abadbae..50832a5c256a 100644
--- a/core/jni/android_graphics_SurfaceTexture.cpp
+++ b/core/jni/android_graphics_SurfaceTexture.cpp
@@ -17,27 +17,24 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceTexture"
-#include <stdio.h>
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-
+#include <com_android_graphics_libgui_flags.h>
+#include <cutils/atomic.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <stdio.h>
#include <surfacetexture/SurfaceTexture.h>
#include <surfacetexture/surface_texture_platform.h>
-
-#include "core_jni_helpers.h"
-
-#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/misc.h>
+#include "core_jni_helpers.h"
#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
// ----------------------------------------------------------------------------
@@ -55,6 +52,7 @@ struct fields_t {
jfieldID producer;
jfieldID frameAvailableListener;
jmethodID postEvent;
+ jmethodID postOnSetFrameRateEvent;
};
static fields_t fields;
@@ -139,61 +137,81 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
// ----------------------------------------------------------------------------
-class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener
-{
+class JNISurfaceTextureContextCommon {
public:
- JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz);
- virtual ~JNISurfaceTextureContext();
- virtual void onFrameAvailable(const BufferItem& item);
+ JNISurfaceTextureContextCommon(JNIEnv* env, jobject weakThiz, jclass clazz)
+ : mWeakThiz(env->NewGlobalRef(weakThiz)), mClazz((jclass)env->NewGlobalRef(clazz)) {}
+
+ virtual ~JNISurfaceTextureContextCommon() {
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->DeleteGlobalRef(mWeakThiz);
+ env->DeleteGlobalRef(mClazz);
+ } else {
+ ALOGW("leaking JNI object references");
+ }
+ }
-private:
- static JNIEnv* getJNIEnv();
+ void onFrameAvailable(const BufferItem& item) {
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
+ } else {
+ ALOGW("onFrameAvailable event will not posted");
+ }
+ }
+
+protected:
+ static JNIEnv* getJNIEnv() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+ if (result != JNI_OK) {
+ ALOGE("thread attach failed: %#x", result);
+ return NULL;
+ }
+ }
+ return env;
+ }
jobject mWeakThiz;
jclass mClazz;
};
-JNISurfaceTextureContext::JNISurfaceTextureContext(JNIEnv* env,
- jobject weakThiz, jclass clazz) :
- mWeakThiz(env->NewGlobalRef(weakThiz)),
- mClazz((jclass)env->NewGlobalRef(clazz))
-{}
-
-JNIEnv* JNISurfaceTextureContext::getJNIEnv() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- JavaVMAttachArgs args = {
- JNI_VERSION_1_4, "JNISurfaceTextureContext", NULL };
- JavaVM* vm = AndroidRuntime::getJavaVM();
- int result = vm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
- if (result != JNI_OK) {
- ALOGE("thread attach failed: %#x", result);
- return NULL;
- }
+class JNISurfaceTextureContextFrameAvailableListener
+ : public JNISurfaceTextureContextCommon,
+ public SurfaceTexture::FrameAvailableListener {
+public:
+ JNISurfaceTextureContextFrameAvailableListener(JNIEnv* env, jobject weakThiz, jclass clazz)
+ : JNISurfaceTextureContextCommon(env, weakThiz, clazz) {}
+ void onFrameAvailable(const BufferItem& item) override {
+ JNISurfaceTextureContextCommon::onFrameAvailable(item);
}
- return env;
-}
+};
-JNISurfaceTextureContext::~JNISurfaceTextureContext()
-{
- JNIEnv* env = getJNIEnv();
- if (env != NULL) {
- env->DeleteGlobalRef(mWeakThiz);
- env->DeleteGlobalRef(mClazz);
- } else {
- ALOGW("leaking JNI object references");
+class JNISurfaceTextureContextListener : public JNISurfaceTextureContextCommon,
+ public SurfaceTexture::SurfaceTextureListener {
+public:
+ JNISurfaceTextureContextListener(JNIEnv* env, jobject weakThiz, jclass clazz)
+ : JNISurfaceTextureContextCommon(env, weakThiz, clazz) {}
+
+ void onFrameAvailable(const BufferItem& item) override {
+ JNISurfaceTextureContextCommon::onFrameAvailable(item);
}
-}
-void JNISurfaceTextureContext::onFrameAvailable(const BufferItem& /* item */)
-{
- JNIEnv* env = getJNIEnv();
- if (env != NULL) {
- env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
- } else {
- ALOGW("onFrameAvailable event will not posted");
+ void onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override {
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->CallStaticVoidMethod(mClazz, fields.postOnSetFrameRateEvent, mWeakThiz, frameRate,
+ compatibility, changeFrameRateStrategy);
+ } else {
+ ALOGW("onSetFrameRate event will not posted");
+ }
}
-}
+};
// ----------------------------------------------------------------------------
@@ -229,6 +247,13 @@ static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
if (fields.postEvent == NULL) {
ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
}
+
+ fields.postOnSetFrameRateEvent =
+ env->GetStaticMethodID(clazz, "postOnSetFrameRateEventFromNative",
+ "(Ljava/lang/ref/WeakReference;FII)V");
+ if (fields.postOnSetFrameRateEvent == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.postOnSetFrameRateEventFromNative");
+ }
}
static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
@@ -274,17 +299,27 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
return;
}
- sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
- clazz));
- surfaceTexture->setFrameAvailableListener(ctx);
- SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
+ if (com::android::graphics::libgui::flags::bq_setframerate()) {
+ sp<JNISurfaceTextureContextListener> ctx(
+ new JNISurfaceTextureContextListener(env, weakThiz, clazz));
+ surfaceTexture->setSurfaceTextureListener(ctx);
+ } else {
+ sp<JNISurfaceTextureContextFrameAvailableListener> ctx(
+ new JNISurfaceTextureContextFrameAvailableListener(env, weakThiz, clazz));
+ surfaceTexture->setFrameAvailableListener(ctx);
+ SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
+ }
}
static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
{
sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
- surfaceTexture->setFrameAvailableListener(0);
- SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
+ if (com::android::graphics::libgui::flags::bq_setframerate()) {
+ surfaceTexture->setSurfaceTextureListener(0);
+ } else {
+ surfaceTexture->setFrameAvailableListener(0);
+ SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
+ }
SurfaceTexture_setSurfaceTexture(env, thiz, 0);
SurfaceTexture_setProducer(env, thiz, 0);
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 041f9c7edeef..bfd80a9e4f74 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -17,19 +17,8 @@
#define LOG_TAG "JavaBinder"
//#define LOG_NDEBUG 0
-#include "android_os_Parcel.h"
#include "android_util_Binder.h"
-#include <atomic>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <mutex>
-#include <stdio.h>
-#include <string>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
#include <android-base/stringprintf.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
@@ -40,7 +29,16 @@
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
#include <cutils/atomic.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/Log.h>
@@ -48,10 +46,11 @@
#include <utils/SystemClock.h>
#include <utils/threads.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include <atomic>
+#include <mutex>
+#include <string>
+#include "android_os_Parcel.h"
#include "core_jni_helpers.h"
//#undef ALOGV
@@ -553,14 +552,48 @@ public:
};
// ----------------------------------------------------------------------------
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+#if __BIONIC__
+#include <android/api-level.h>
+static bool target_sdk_is_at_least_vic() {
+ return android_get_application_target_sdk_version() >= __ANDROID_API_V__;
+}
+#else
+static constexpr bool target_sdk_is_at_least_vic() {
+ // If not built for Android (i.e. glibc host), follow the latest behavior as there's no compat
+ // requirement there.
+ return true;
+}
+#endif // __BIONIC__
+#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
class JavaDeathRecipient : public IBinder::DeathRecipient
{
public:
JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
- : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
- mObjectWeak(NULL), mList(list)
- {
+ : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+ // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
+ // to the death recipient objects. This is to prevent the memory leak which can happen when
+ // the death recipient object internally has a strong reference to the proxy object. Under
+ // the old behavior, you were unable to kill the binder service by dropping all references
+ // to the proxy object - because it is still strong referenced from JNI (here). The only way
+ // to cut the strong reference was to call unlinkDeath(), but it was easy to forget.
+ //
+ // Now, the strong reference to the death recipient is held in the Java-side proxy object.
+ // See BinderProxy.mDeathRecipients. From JNI, only the weak reference is kept. An
+ // implication of this is that you may not receive binderDied() if you drop all references
+ // to the proxy object before the service dies. This should be okay for most cases because
+ // you normally are not interested in the death of a binder service which you don't have any
+ // reference to. If however you want to get binderDied() regardless of the proxy object's
+ // lifecycle, keep a strong reference to the death recipient object by yourself.
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+ if (target_sdk_is_at_least_vic()) {
+ mObjectWeak = env->NewWeakGlobalRef(object);
+ } else
+#endif
+ {
+ mObject = env->NewGlobalRef(object);
+ }
// These objects manage their own lifetimes so are responsible for final bookkeeping.
// The list holds a strong reference to this object.
LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
@@ -573,26 +606,49 @@ public:
void binderDied(const wp<IBinder>& who)
{
LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
- if (mObject != NULL) {
- JNIEnv* env = javavm_to_jnienv(mVM);
- ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
- env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mSendDeathNotice, mObject,
- jBinderProxy.get());
- if (env->ExceptionCheck()) {
- jthrowable excep = env->ExceptionOccurred();
- binder_report_exception(env, excep,
- "*** Uncaught exception returned from death notification!");
- }
+ if (mObject == NULL && mObjectWeak == NULL) {
+ return;
+ }
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
+
+ // Hold a local reference to the recipient. This may fail if the recipient is weakly
+ // referenced, in which case we can't deliver the death notice.
+ ScopedLocalRef<jobject> jRecipient(env,
+ env->NewLocalRef(mObject != NULL ? mObject
+ : mObjectWeak));
+ if (jRecipient.get() == NULL) {
+ ALOGW("Binder died, but death recipient is already garbage collected. If your target "
+ "sdk level is at or above 35, this can happen when you dropped all references to "
+ "the binder service before it died. If you want to get a death notice for a "
+ "binder service which you have no reference to, keep a strong reference to the "
+ "death recipient by yourself.");
+ return;
+ }
- // Serialize with our containing DeathRecipientList so that we can't
- // delete the global ref on mObject while the list is being iterated.
+ if (mFired) {
+ ALOGW("Received multiple death notices for the same binder object. Binder driver bug?");
+ return;
+ }
+ mFired = true;
+
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice,
+ jRecipient.get(), jBinderProxy.get());
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from death notification!");
+ }
+
+ // Demote from strong ref (if exists) to weak after binderDied() has been delivered, to
+ // allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. Do this in sync
+ // with our containing DeathRecipientList so that we can't delete the global ref on mObject
+ // while the list is being iterated.
+ if (mObject != NULL) {
sp<DeathRecipientList> list = mList.promote();
if (list != NULL) {
AutoMutex _l(list->lock());
- // Demote from strong ref to weak after binderDied() has been delivered,
- // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
mObjectWeak = env->NewWeakGlobalRef(mObject);
env->DeleteGlobalRef(mObject);
mObject = NULL;
@@ -659,9 +715,19 @@ protected:
private:
JavaVM* const mVM;
- jobject mObject; // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied().
- jweak mObjectWeak; // Weak ref to the same Java-side DeathRecipient after binderDied().
+
+ // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
+ // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
+ // a weak reference (mObjectWeak).
+ // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
+ // always). Instead, the strong reference to the Java-side DeathRecipient is made in
+ // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
+ jobject mObject;
+ jweak mObjectWeak;
wp<DeathRecipientList> mList;
+
+ // Whether binderDied was called or not.
+ bool mFired = false;
};
// ----------------------------------------------------------------------------
@@ -1435,17 +1501,19 @@ static jobject android_os_BinderProxy_getExtension(JNIEnv* env, jobject obj) {
// ----------------------------------------------------------------------------
+// clang-format off
static const JNINativeMethod gBinderProxyMethods[] = {
/* name, signature, funcPtr */
{"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
{"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
{"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
- {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
- {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
+ {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
+// clang-format on
const char* const kBinderProxyPathName = "android/os/BinderProxy";
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 69fc515444b2..41c65aec3144 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -38,6 +38,7 @@ static struct {
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
+ jmethodID dispatchHotplugConnectionError;
jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
@@ -89,6 +90,7 @@ private:
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchHotplugConnectionError(nsecs_t timestamp, int errorCode) override;
void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
nsecs_t renderPeriod) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -230,6 +232,22 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
+void NativeDisplayEventReceiver::dispatchHotplugConnectionError(nsecs_t timestamp,
+ int connectionError) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking hotplug dispatchHotplugConnectionError handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError,
+ timestamp, connectionError);
+ ALOGV("receiver %p ~ Returned from hotplug dispatchHotplugConnectionError handler.", this);
+ }
+
+ mMessageQueue->raiseAndClearException(env, "dispatchHotplugConnectionError");
+}
+
void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t modeId, nsecs_t renderPeriod) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -354,8 +372,12 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
gDisplayEventReceiverClassInfo.dispatchVsync =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
- gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchHotplug =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug",
+ "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
+ "dispatchHotplugConnectionError", "(JI)V");
gDisplayEventReceiverClassInfo.dispatchModeChanged =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
"(JJIJ)V");
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d0af3dbde3d..4e073ca3863e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4830,7 +4830,7 @@
<string translatable="false" name="config_deviceSpecificInputMethodManagerService"></string>
<!-- Component name of media projection permission dialog -->
- <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
+ <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity</string>
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">28dp</dimen>
@@ -5227,6 +5227,28 @@
non-zero. -->
<integer name="config_defaultPeakRefreshRate">0</integer>
+ <!-- External display peak refresh rate for the given device. Change this value if you want to
+ prevent the framework from using higher refresh rates, even if display modes with higher
+ refresh rates are available from hardware composer. Only has an effect if this value and
+ config_externalDisplayPeakWidth and config_externalDisplayPeakHeight are non-zero. -->
+ <integer name="config_externalDisplayPeakRefreshRate">0</integer>
+
+ <!-- External display peak width for the given device. Change this value if you want
+ to prevent the framework from using higher resolution, even if display modes with higher
+ resolutions are available from hardware composer. Only has an effect if this value and
+ config_externalDisplayPeakRefreshRate and config_externalDisplayPeakHeight are non-zero.-->
+ <integer name="config_externalDisplayPeakWidth">0</integer>
+
+ <!-- External display peak height for the given device. Change this value if you want
+ to prevent the framework from using higher resolution, even if display modes with higher
+ resolutions are available from hardware composer. Only has an effect if this value and
+ config_externalDisplayPeakRefreshRate and config_externalDisplayPeakWidth are non-zero. -->
+ <integer name="config_externalDisplayPeakHeight">0</integer>
+
+ <!-- Enable synchronization of the displays refresh rates by applying the default low refresh
+ rate. -->
+ <bool name="config_refreshRateSynchronizationEnabled">false</bool>
+
<!-- The display uses different gamma curves for different refresh rates. It's hard for panel
vendors to tune the curves to have exact same brightness for different refresh rate. So
flicker could be observed at switch time. The issue is worse at the gamma lower end.
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 878e6b306571..71d696ea4554 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -172,28 +172,17 @@
<integer name="config_satellite_nb_iot_inactivity_timeout_millis">180000</integer>
<java-symbol type="integer" name="config_satellite_nb_iot_inactivity_timeout_millis" />
- <!-- Telephony config for services supported by satellite providers. The format of each config
- string in the array is as follows: "PLMN_1:service_1,service_2,..."
- where PLMN is the satellite PLMN of a provider and service is an integer with the
- following value:
- 1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}
- 2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}
- 3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS}
- 4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO}
- 5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}
- 6 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_MMS}
- Example of a config string: "10011:2,3"
-
- The PLMNs not configured in this array will be ignored and will not be used for satellite
- scanning. -->
- <string-array name="config_satellite_services_supported_by_providers" translatable="false">
- </string-array>
- <java-symbol type="array" name="config_satellite_services_supported_by_providers" />
+ <!-- Telephony config for the PLMNs of all satellite providers. This is used by satellite modem
+ to identify providers that should be ignored if the carrier config
+ carrier_supported_satellite_services_per_provider_bundle does not support them.
+ -->
+ <string-array name="config_satellite_providers" translatable="false"></string-array>
+ <java-symbol type="array" name="config_satellite_providers" />
- <!-- The identifier of the satellite's eSIM profile preloaded on the device. The identifier is
- composed of MCC and MNC of the satellite PLMN with the format "mccmnc". -->
- <string name="config_satellite_esim_identifier" translatable="false"></string>
- <java-symbol type="string" name="config_satellite_esim_identifier" />
+ <!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC
+ of the satellite PLMN with the format "mccmnc". -->
+ <string name="config_satellite_sim_identifier" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_sim_identifier" />
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 83fb0986a19f..b0eee1cecc89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4231,6 +4231,10 @@
<!-- For high refresh rate displays -->
<java-symbol type="integer" name="config_defaultRefreshRate" />
<java-symbol type="integer" name="config_defaultPeakRefreshRate" />
+ <java-symbol type="integer" name="config_externalDisplayPeakRefreshRate" />
+ <java-symbol type="integer" name="config_externalDisplayPeakWidth" />
+ <java-symbol type="integer" name="config_externalDisplayPeakHeight" />
+ <java-symbol type="bool" name="config_refreshRateSynchronizationEnabled" />
<java-symbol type="integer" name="config_defaultRefreshRateInZone" />
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 48dc167438da..7f3e01432dd9 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -69,6 +69,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -95,16 +96,20 @@ import android.util.Pair;
import android.widget.RemoteViews;
import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.util.ContrastColorUtil;
import junit.framework.Assert;
+import libcore.junit.util.compat.CoreCompatChangeRule;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import java.util.List;
@@ -116,6 +121,9 @@ public class NotificationTest {
private Context mContext;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
@@ -1777,6 +1785,42 @@ public class NotificationTest {
assertThat(recoveredExtender.getColor()).isEqualTo(1234);
}
+ @Test
+ @CoreCompatChangeRule.EnableCompatChanges({Notification.WEARABLE_EXTENDER_BACKGROUND_BLOCKED})
+ public void wearableBackgroundBlockEnabled_wearableBackgroundSet_valueRemainsNull() {
+ Notification.WearableExtender extender = new Notification.WearableExtender();
+ Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+ extender.setBackground(bitmap);
+ Notification notif =
+ new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test_title")
+ .extend(extender)
+ .build();
+
+ Notification.WearableExtender result = new Notification.WearableExtender(notif);
+ Assert.assertNull(result.getBackground());
+ }
+
+ @Test
+ @CoreCompatChangeRule.DisableCompatChanges({Notification.WEARABLE_EXTENDER_BACKGROUND_BLOCKED})
+ public void wearableBackgroundBlockDisabled_wearableBackgroundSet_valueKeepsBitmap() {
+ Notification.WearableExtender extender = new Notification.WearableExtender();
+ Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+ extender.setBackground(bitmap);
+ Notification notif =
+ new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test_title")
+ .extend(extender)
+ .build();
+
+ Notification.WearableExtender result = new Notification.WearableExtender(notif);
+ Bitmap resultBitmap = result.getBackground();
+ assertNotNull(resultBitmap);
+ Assert.assertEquals(bitmap, resultBitmap);
+ }
+
private void assertValid(Notification.Colors c) {
// Assert that all colors are populated
assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
diff --git a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
new file mode 100644
index 000000000000..ecd75a8370ce
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link DestroyActivityItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:DestroyActivityItemTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DestroyActivityItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mActivityToken;
+
+ // Can't mock final class.
+ private ActivityClientRecord mActivityClientRecord;
+
+ private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
+ private DestroyActivityItem mItem;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mItem = DestroyActivityItem.obtain(
+ mActivityToken, false /* finished */, 123 /* configChanges */);
+ mActivityClientRecord = new ActivityClientRecord();
+ mActivitiesToBeDestroyed = new ArrayMap<>();
+
+ doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
+ }
+
+ @Test
+ public void testPreExecute() {
+ mItem.preExecute(mHandler);
+
+ assertEquals(1, mActivitiesToBeDestroyed.size());
+ assertEquals(mItem, mActivitiesToBeDestroyed.get(mActivityToken));
+ }
+
+ @Test
+ public void testPostExecute() {
+ mItem.preExecute(mHandler);
+ mItem.postExecute(mHandler, mPendingActions);
+
+ assertTrue(mActivitiesToBeDestroyed.isEmpty());
+ }
+
+ @Test
+ public void testExecute() {
+ mItem.execute(mHandler, mActivityClientRecord, mPendingActions);
+
+ verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
+ eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index a1a2bdbe0f15..44a4d580dbc0 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -28,6 +28,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
@@ -247,7 +248,7 @@ public class TransactionExecutorTests {
@Test
public void testDoNotLaunchDestroyedActivity() {
- final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed = new ArrayMap<>();
+ final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
// Assume launch transaction is still in queue, so there is no client record.
when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
@@ -259,7 +260,7 @@ public class TransactionExecutorTests {
DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
destroyTransaction.preExecute(mTransactionHandler);
// The activity should be added to to-be-destroyed container.
- assertEquals(1, mTransactionHandler.getActivitiesToBeDestroyed().size());
+ assertEquals(1, activitiesToBeDestroyed.size());
// A previous queued launch transaction runs on main thread (execute).
final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
@@ -274,7 +275,7 @@ public class TransactionExecutorTests {
// After the destroy transaction has been executed, the token should be removed.
mExecutor.execute(destroyTransaction);
- assertEquals(0, mTransactionHandler.getActivitiesToBeDestroyed().size());
+ assertTrue(activitiesToBeDestroyed.isEmpty());
}
@Test
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index e082c25fa499..c7eddabefc11 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -17,7 +17,6 @@
package com.android.internal.content;
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,6 +27,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -36,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -45,6 +46,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class PackageMonitorTest {
private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp";
+ private static final String FAKE_EXTRA_REASON = "android.intent.extra.fakereason";
private static final int FAKE_PACKAGE_UID = 123;
private static final int FAKE_USER_ID = 0;
private static final int WAIT_CALLBACK_CALLED_IN_MS = 300;
@@ -245,6 +247,7 @@ public class PackageMonitorTest {
intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REASON, FAKE_EXTRA_REASON);
String [] packageList = new String[]{FAKE_PACKAGE_NAME};
intent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, packageList);
spyPackageMonitor.doHandlePackageEvent(intent);
@@ -253,6 +256,20 @@ public class PackageMonitorTest {
verify(spyPackageMonitor, times(1))
.onPackageChanged(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eq(packageList));
verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageChangedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REASON))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REASON));
+
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
}
@@ -272,6 +289,21 @@ public class PackageMonitorTest {
verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
verify(spyPackageMonitor, times(1))
.onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
verify(spyPackageMonitor, times(1))
.onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
@@ -295,6 +327,21 @@ public class PackageMonitorTest {
.onPackageRemoved(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
verify(spyPackageMonitor, times(1))
.onPackageRemovedAllUsers(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME),
eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -316,6 +363,19 @@ public class PackageMonitorTest {
verify(spyPackageMonitor, times(1))
.onPackageUpdateFinished(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
verify(spyPackageMonitor, times(1))
.onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -336,6 +396,19 @@ public class PackageMonitorTest {
verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
verify(spyPackageMonitor, times(1))
.onPackageAdded(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME),
eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ad0ead78f492..9c6528785584 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1123,12 +1123,6 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
- "-1076978367": {
- "message": "thawRotation: mRotation=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1075136930": {
"message": "startLockTaskMode: Can't lock due to auth",
"level": "WARN",
@@ -1231,6 +1225,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-962760979": {
+ "message": "thawRotation: mRotation=%d, caller=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-961053385": {
"message": "attachWindowContextToDisplayArea: calling from non-existing process pid=%d uid=%d",
"level": "WARN",
@@ -2779,6 +2779,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "364992694": {
+ "message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"371173718": {
"message": "finishSync cancel=%b for %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index dfe5012a2b4d..dd82fed03087 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,8 +25,10 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Trace;
import android.view.Surface;
import android.view.TextureView;
+import android.view.flags.Flags;
import java.lang.ref.WeakReference;
@@ -79,6 +82,7 @@ public class SurfaceTexture {
private final Looper mCreatorLooper;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Handler mOnFrameAvailableHandler;
+ private Handler mOnSetFrameRateHandler;
/**
* These fields are used by native code, do not access or modify.
@@ -100,6 +104,21 @@ public class SurfaceTexture {
}
/**
+ * Callback interface for being notified that a producer set a frame rate
+ * @hide
+ */
+ public interface OnSetFrameRateListener {
+ /**
+ * Called when the producer sets a frame rate
+ * @hide
+ */
+ void onSetFrameRate(SurfaceTexture surfaceTexture,
+ @FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility,
+ @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy);
+ }
+
+ /**
* Exception thrown when a SurfaceTexture couldn't be created or resized.
*
* @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException}
@@ -224,6 +243,48 @@ public class SurfaceTexture {
}
}
+ private static class SetFrameRateArgs {
+ SetFrameRateArgs(@FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility,
+ @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
+ this.mFrameRate = frameRate;
+ this.mCompatibility = compatibility;
+ this.mChangeFrameRateStrategy = changeFrameRateStrategy;
+ }
+ final float mFrameRate;
+ final int mCompatibility;
+ final int mChangeFrameRateStrategy;
+ }
+
+ /**
+ * Register a callback to be invoked when the producer sets a frame rate using
+ * Surface.setFrameRate.
+ * @hide
+ */
+ public void setOnSetFrameRateListener(@Nullable final OnSetFrameRateListener listener,
+ @Nullable Handler handler) {
+ if (listener != null) {
+ Looper looper = handler != null ? handler.getLooper() :
+ mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();
+ mOnSetFrameRateHandler = new Handler(looper, null, true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSetFrameRateHandler");
+ try {
+ SetFrameRateArgs args = (SetFrameRateArgs) msg.obj;
+ listener.onSetFrameRate(SurfaceTexture.this,
+ args.mFrameRate, args.mCompatibility,
+ args.mChangeFrameRateStrategy);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+ };
+ } else {
+ mOnSetFrameRateHandler = null;
+ }
+ }
+
/**
* Set the default size of the image buffers. The image producer may override the buffer size,
* in which case the producer-set buffer size will be used, not the default size set by this
@@ -418,6 +479,35 @@ public class SurfaceTexture {
}
/**
+ * This method is invoked from native code only.
+ * @hide
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ private static void postOnSetFrameRateEventFromNative(WeakReference<SurfaceTexture> weakSelf,
+ @FloatRange(from = 0.0) float frameRate,
+ @Surface.FrameRateCompatibility int compatibility,
+ @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "postOnSetFrameRateEventFromNative");
+ try {
+ if (Flags.toolkitSetFrameRate()) {
+ SurfaceTexture st = weakSelf.get();
+ if (st != null) {
+ Handler handler = st.mOnSetFrameRateHandler;
+ if (handler != null) {
+ Message msg = new Message();
+ msg.obj = new SetFrameRateArgs(frameRate, compatibility,
+ changeFrameRateStrategy);
+ handler.sendMessage(msg);
+ }
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
+ }
+
+ /**
* Returns {@code true} if the SurfaceTexture is single-buffered.
* @hide
*/
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 395624145cd5..96c257b304a0 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -16,6 +16,7 @@
package android.security.keystore;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,7 +35,10 @@ import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
@@ -300,6 +304,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private final Date mKeyValidityForConsumptionEnd;
private final @KeyProperties.PurposeEnum int mPurposes;
private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests;
private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private final @KeyProperties.BlockModeEnum String[] mBlockModes;
@@ -345,6 +350,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
Date keyValidityForConsumptionEnd,
@KeyProperties.PurposeEnum int purposes,
@KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.DigestEnum Set<String> mgf1Digests,
@KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
@KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
@KeyProperties.BlockModeEnum String[] blockModes,
@@ -404,6 +410,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
mPurposes = purposes;
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ // No need to copy the input parameter because the Builder class passes in an immutable
+ // collection.
+ mMgf1Digests = mgf1Digests != null ? mgf1Digests : Collections.emptySet();
mEncryptionPaddings =
ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
@@ -566,7 +575,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
/**
* Returns the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the
- * key can be used or {@code null} if not specified.
+ * key can be used.
*
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*
@@ -594,6 +603,40 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * Returns the set of digests that can be used by the MGF1 mask generation function
+ * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP}
+ * scheme.
+ * If not explicitly specified during key generation, the default {@code SHA-1} digest is
+ * used and may be specified when using the key.
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ *
+ * @throws IllegalStateException if this set has not been specified.
+ *
+ * @see #isMgf1DigestsSpecified()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
+ if (mMgf1Digests.isEmpty()) {
+ throw new IllegalStateException("Mask generation function (MGF) not specified");
+ }
+ return new HashSet(mMgf1Digests);
+ }
+
+ /**
+ * Returns {@code true} if the set of digests for the MGF1 mask generation function,
+ * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme.
+ *
+ * @see #getMgf1Digests()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public boolean isMgf1DigestsSpecified() {
+ return !mMgf1Digests.isEmpty();
+ }
+
+ /**
* Returns the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OEAPPadding},
* {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
* encrypting/decrypting. Attempts to use the key with any other padding scheme will be
@@ -913,6 +956,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyProperties.DigestEnum String[] mDigests;
+ private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests =
+ Collections.emptySet();
private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private @KeyProperties.BlockModeEnum String[] mBlockModes;
@@ -983,6 +1028,9 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
if (sourceSpec.isDigestsSpecified()) {
mDigests = sourceSpec.getDigests();
}
+ if (sourceSpec.isMgf1DigestsSpecified()) {
+ mMgf1Digests = sourceSpec.getMgf1Digests();
+ }
mEncryptionPaddings = sourceSpec.getEncryptionPaddings();
mSignaturePaddings = sourceSpec.getSignaturePaddings();
mBlockModes = sourceSpec.getBlockModes();
@@ -1230,6 +1278,30 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be
+ * used by the mask generation function MGF1 (which is used for certain operations with
+ * the key). Attempts to use the key with any other digest for the mask generation
+ * function will be rejected.
+ *
+ * <p>This can only be specified for signing/verification keys and RSA encryption/decryption
+ * keys used with RSA OAEP padding scheme because these operations involve a mask generation
+ * function (MGF1) with a digest.
+ * The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation
+ * time if no digests have been explicitly provided.
+ * When using the key, the caller may not specify any digests that were not provided during
+ * key creation time. The caller may specify the default digest, {@code SHA-1}, if no
+ * digests were explicitly provided during key creation (but it is not necessary to do so).
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+ mMgf1Digests = Set.of(mgf1Digests);
+ return this;
+ }
+
+ /**
* Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding},
* {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
* encrypting/decrypting. Attempts to use the key with any other padding scheme will be
@@ -1782,6 +1854,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mKeyValidityForConsumptionEnd,
mPurposes,
mDigests,
+ mMgf1Digests,
mEncryptionPaddings,
mSignaturePaddings,
mBlockModes,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 5ab21bc5f489..c1e3bab5d37c 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -16,6 +16,7 @@
package android.security.keystore;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,7 +31,10 @@ import java.security.Key;
import java.security.KeyStore.ProtectionParameter;
import java.security.Signature;
import java.security.cert.Certificate;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -223,6 +227,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private final @KeyProperties.DigestEnum String[] mDigests;
+ private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests;
private final @KeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
@@ -247,6 +252,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
@KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
@KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
@KeyProperties.DigestEnum String[] digests,
+ @KeyProperties.DigestEnum Set<String> mgf1Digests,
@KeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
@@ -271,6 +277,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
mSignaturePaddings =
ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+ mMgf1Digests = mgf1Digests;
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
mUserAuthenticationRequired = userAuthenticationRequired;
@@ -381,6 +388,40 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
}
/**
+ * Returns the set of digests that can be used by the MGF1 mask generation function
+ * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP}
+ * scheme.
+ * If not explicitly specified during key generation, the default {@code SHA-1} digest is
+ * used and may be specified.
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ *
+ * @throws IllegalStateException if this set has not been specified.
+ *
+ * @see #isMgf1DigestsSpecified()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
+ if (mMgf1Digests.isEmpty()) {
+ throw new IllegalStateException("Mask generation function (MGF) not specified");
+ }
+ return new HashSet(mMgf1Digests);
+ }
+
+ /**
+ * Returns {@code true} if the set of digests for the MGF1 mask generation function,
+ * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme.
+ *
+ * @see #getMgf1Digests()
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public boolean isMgf1DigestsSpecified() {
+ return !mMgf1Digests.isEmpty();
+ }
+
+ /**
* Gets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be used
* when encrypting/decrypting. Attempts to use the key with any other block modes will be
* rejected.
@@ -588,6 +629,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
private @KeyProperties.DigestEnum String[] mDigests;
+ private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests =
+ Collections.emptySet();
private @KeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
@@ -739,6 +782,30 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
}
/**
+ * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be
+ * used by the mask generation function MGF1 (which is used for certain operations with
+ * the key). Attempts to use the key with any other digest for the mask generation
+ * function will be rejected.
+ *
+ * <p>This can only be specified for signing/verification keys and RSA encryption/decryption
+ * keys used with RSA OAEP padding scheme because these operations involve a mask generation
+ * function (MGF1) with a digest.
+ * The default digest for MGF1 is {@code SHA-1}, which will be specified during key import
+ * time if no digests have been explicitly provided.
+ * When using the key, the caller may not specify any digests that were not provided during
+ * key import time. The caller may specify the default digest, {@code SHA-1}, if no
+ * digests were explicitly provided during key import (but it is not necessary to do so).
+ *
+ * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+ */
+ @NonNull
+ @FlaggedApi("MGF1_DIGEST_SETTER")
+ public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+ mMgf1Digests = Set.of(mgf1Digests);
+ return this;
+ }
+
+ /**
* Sets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be
* used when encrypting/decrypting. Attempts to use the key with any other block modes will
* be rejected.
@@ -1141,6 +1208,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
mEncryptionPaddings,
mSignaturePaddings,
mDigests,
+ mMgf1Digests,
mBlockModes,
mRandomizedEncryptionRequired,
mUserAuthenticationRequired,
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 9356eb85bd8a..ceba04efa65d 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -23,7 +23,11 @@ import java.math.BigInteger;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
+import java.util.Set;
import javax.security.auth.x500.X500Principal;
@@ -91,6 +95,11 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
} else {
out.writeStringArray(null);
}
+ if (mSpec.isMgf1DigestsSpecified()) {
+ out.writeStringList(List.copyOf(mSpec.getMgf1Digests()));
+ } else {
+ out.writeStringList(null);
+ }
out.writeStringArray(mSpec.getEncryptionPaddings());
out.writeStringArray(mSpec.getSignaturePaddings());
out.writeStringArray(mSpec.getBlockModes());
@@ -153,6 +162,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
final Date keyValidityForOriginationEnd = readDateOrNull(in);
final Date keyValidityForConsumptionEnd = readDateOrNull(in);
final String[] digests = in.createStringArray();
+ final ArrayList<String> mgf1Digests = in.createStringArrayList();
final String[] encryptionPaddings = in.createStringArray();
final String[] signaturePaddings = in.createStringArray();
final String[] blockModes = in.createStringArray();
@@ -191,6 +201,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
keyValidityForConsumptionEnd,
purposes,
digests,
+ mgf1Digests != null ? Set.copyOf(mgf1Digests) : Collections.emptySet(),
encryptionPaddings,
signaturePaddings,
blockModes,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 9ac0f6d304f6..101a10e3d312 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -24,6 +24,7 @@ import android.os.StrictMode;
import android.security.KeyStoreException;
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
import android.security.keystore.KeyStoreCryptoOperation;
import android.system.keystore2.Authorization;
@@ -71,7 +72,7 @@ import javax.crypto.spec.SecretKeySpec;
*/
abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
private static final String TAG = "AndroidKeyStoreCipherSpiBase";
- public static final String DEFAULT_MGF1_DIGEST = "SHA-1";
+ public static final String DEFAULT_MGF1_DIGEST = KeyProperties.DIGEST_SHA1;
// Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
// doFinal finishes.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 1398da3f5ef2..ed4b485f3927 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -188,6 +188,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private int[] mKeymasterEncryptionPaddings;
private int[] mKeymasterSignaturePaddings;
private int[] mKeymasterDigests;
+ private int[] mKeymasterMgf1Digests;
private Long mRSAPublicExponent;
@@ -323,6 +324,21 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
} else {
mKeymasterDigests = EmptyArray.INT;
}
+ if (spec.isMgf1DigestsSpecified()) {
+ // User-specified digests: Add all of them and do _not_ add the SHA-1
+ // digest by default (stick to what the user provided).
+ Set<String> mgfDigests = spec.getMgf1Digests();
+ mKeymasterMgf1Digests = new int[mgfDigests.size()];
+ int offset = 0;
+ for (String digest : mgfDigests) {
+ mKeymasterMgf1Digests[offset] = KeyProperties.Digest.toKeymaster(digest);
+ offset++;
+ }
+ } else {
+ // No user-specified digests: Add the SHA-1 default.
+ mKeymasterMgf1Digests = new int[]{
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)};
+ }
// Check that user authentication related parameters are acceptable. This method
// will throw an IllegalStateException if there are issues (e.g., secure lock screen
@@ -544,6 +560,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mKeymasterEncryptionPaddings = null;
mKeymasterSignaturePaddings = null;
mKeymasterDigests = null;
+ mKeymasterMgf1Digests = null;
mKeySizeBits = 0;
mSpec = null;
mRSAPublicExponent = null;
@@ -831,24 +848,11 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
KeymasterDefs.KM_TAG_PADDING, padding
));
if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
- final boolean[] hasDefaultMgf1DigestBeenAdded = {false};
- ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+ ArrayUtils.forEach(mKeymasterMgf1Digests, (mgf1Digest) -> {
params.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest
));
- hasDefaultMgf1DigestBeenAdded[0] |=
- digest.equals(KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST));
});
- /* Because of default MGF1 digest is SHA-1. It has to be added in Key
- * characteristics. Otherwise, crypto operations will fail with Incompatible
- * MGF1 digest.
- */
- if (!hasDefaultMgf1DigestBeenAdded[0]) {
- params.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
- KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
- ));
- }
}
});
ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 273dff1cb8b3..ddbd93e458fd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -526,25 +526,22 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
padding
));
if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
- if (spec.isDigestsSpecified()) {
- boolean hasDefaultMgf1DigestBeenAdded = false;
- for (String digest : spec.getDigests()) {
+ if (spec.isMgf1DigestsSpecified()) {
+ for (String mgf1Digest : spec.getMgf1Digests()) {
importArgs.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
- KeyProperties.Digest.toKeymaster(digest)
+ KeyProperties.Digest.toKeymaster(mgf1Digest)
));
- hasDefaultMgf1DigestBeenAdded |= digest.equals(DEFAULT_MGF1_DIGEST);
}
+ } else {
/* Because of default MGF1 digest is SHA-1. It has to be added in Key
* characteristics. Otherwise, crypto operations will fail with Incompatible
* MGF1 digest.
*/
- if (!hasDefaultMgf1DigestBeenAdded) {
- importArgs.add(KeyStore2ParameterUtils.makeEnum(
- KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
- KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
- ));
- }
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
+ ));
}
}
}
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 2ae61ab3b38d..d4e2dbc81509 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -17,6 +17,7 @@
package android.security;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -101,6 +102,7 @@ public final class ParcelableKeyGenParameterSpecTest {
assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+ assertThat(spec.isMgf1DigestsSpecified(), is(false));
assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
@@ -189,4 +191,19 @@ public final class ParcelableKeyGenParameterSpecTest {
ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
assertEquals(parcelSpec.getName(), ecSpec.getName());
}
+
+ @Test
+ public void testParcelingMgf1Digests() {
+ String[] mgf1Digests =
+ new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+
+ ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+ new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(mgf1Digests)
+ .build());
+ Parcel parcel = parcelForReading(spec);
+ KeyGenParameterSpec fromParcel =
+ ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+ assertArrayEquals(fromParcel.getMgf1Digests().toArray(), mgf1Digests);
+ }
}
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
index ddbb1d8c097c..da5e8bf84191 100644
--- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -16,9 +16,12 @@
package android.security.keystore;
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
import android.security.ParcelableKeyGenParameterSpecTest;
@@ -61,4 +64,54 @@ public final class KeyGenParameterSpecTest {
assertEquals(copiedSpec.getAttestationChallenge(), null);
}
+
+ @Test
+ public void testMgf1DigestsNotSpecifiedByDefault() {
+ KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+ assertThat(spec.isMgf1DigestsSpecified(), is(false));
+ assertThrows(IllegalStateException.class, () -> {
+ spec.getMgf1Digests();
+ });
+ }
+
+ @Test
+ public void testMgf1DigestsCanBeSpecified() {
+ String[] mgf1Digests =
+ new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(mgf1Digests)
+ .build();
+ assertThat(spec.isMgf1DigestsSpecified(), is(true));
+ assertThat(spec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+
+ KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build();
+ assertThat(copiedSpec.isMgf1DigestsSpecified(), is(true));
+ assertThat(copiedSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+ }
+
+ @Test
+ public void testMgf1DigestsAreNotModified() {
+ String[] mgf1Digests =
+ new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(mgf1Digests);
+
+ KeyGenParameterSpec firstSpec = builder.build();
+ assertArrayEquals(mgf1Digests, firstSpec.getMgf1Digests().toArray());
+
+ String[] otherDigests = new String[] {KeyProperties.DIGEST_SHA224};
+ KeyGenParameterSpec secondSpec = builder.setMgf1Digests(otherDigests).build();
+ assertThat(secondSpec.getMgf1Digests(), containsInAnyOrder(otherDigests));
+
+ // Now check that the first spec created hasn't changed.
+ assertThat(firstSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+ }
+
+ @Test
+ public void testEmptyMgf1DigestsCanBeSet() {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setMgf1Digests(new String[] {}).build();
+
+ assertThat(spec.isMgf1DigestsSpecified(), is(false));
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index 5eeb3b650074..b141bebbe8b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -18,12 +18,16 @@ package com.android.wm.shell.compatui;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -36,14 +40,29 @@ import com.android.wm.shell.R;
*/
public class UserAspectRatioSettingsLayout extends LinearLayout {
+ private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+
+ private static final Interpolator PATH_INTERPOLATOR =
+ new PathInterpolator(0.2f, 0f, 0f, 1f);
+
private static final float ALPHA_FULL_TRANSPARENT = 0f;
private static final float ALPHA_FULL_OPAQUE = 1f;
- private static final long VISIBILITY_ANIMATION_DURATION_MS = 50;
+ private static final float SCALE_START = 0.8f;
+
+ private static final float SCALE_END = 1f;
+
+ private static final long FADE_ANIMATION_DURATION_MS = 167;
+
+ private static final long SCALE_ANIMATION_DURATION_MS = 300;
private static final String ALPHA_PROPERTY_NAME = "alpha";
+ private static final String SCALE_X_PROPERTY_NAME = "scaleX";
+
+ private static final String SCALE_Y_PROPERTY_NAME = "scaleY";
+
private UserAspectRatioSettingsWindowManager mWindowManager;
public UserAspectRatioSettingsLayout(Context context) {
@@ -88,7 +107,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout {
if (show) {
showItem(view);
} else {
- view.setVisibility(visibility);
+ hideItem(view);
}
}
@@ -121,16 +140,40 @@ public class UserAspectRatioSettingsLayout extends LinearLayout {
}
private void showItem(@NonNull View view) {
- view.setVisibility(View.VISIBLE);
+ final AnimatorSet animatorSet = new AnimatorSet();
final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE);
- fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
- fadeIn.addListener(new AnimatorListenerAdapter() {
+ fadeIn.setDuration(FADE_ANIMATION_DURATION_MS);
+ fadeIn.setInterpolator(LINEAR_INTERPOLATOR);
+ final ObjectAnimator scaleY =
+ ObjectAnimator.ofFloat(view, SCALE_Y_PROPERTY_NAME, SCALE_START, SCALE_END);
+ final ObjectAnimator scaleX =
+ ObjectAnimator.ofFloat(view, SCALE_X_PROPERTY_NAME, SCALE_START, SCALE_END);
+ scaleX.setDuration(SCALE_ANIMATION_DURATION_MS);
+ scaleX.setInterpolator(PATH_INTERPOLATOR);
+ scaleY.setDuration(SCALE_ANIMATION_DURATION_MS);
+ scaleY.setInterpolator(PATH_INTERPOLATOR);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationStart(Animator animation) {
view.setVisibility(View.VISIBLE);
}
});
- fadeIn.start();
+ animatorSet.playTogether(fadeIn, scaleY, scaleX);
+ animatorSet.start();
+ }
+
+ private void hideItem(@NonNull View view) {
+ final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
+ ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT);
+ fadeOut.setDuration(FADE_ANIMATION_DURATION_MS);
+ fadeOut.setInterpolator(LINEAR_INTERPOLATOR);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ }
+ });
+ fadeOut.start();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f8dd208f96db..09ba4f79326e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -37,6 +37,7 @@ import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.RemoteTransition
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
@@ -65,7 +66,9 @@ import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.ShellSharedConstants
+import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.util.KtProtoLog
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -139,20 +142,22 @@ class DesktopTasksController(
}
/** Show all tasks, that are part of the desktop, on top of launcher */
- fun showDesktopApps(displayId: Int) {
+ fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
val wct = WindowContainerTransaction()
- // TODO(b/278084491): pass in display id
bringDesktopAppsToFront(displayId, wct)
- // Execute transaction if there are pending operations
- if (!wct.isEmpty) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // TODO(b/268662477): add animation for the transition
- transitions.startTransition(TRANSIT_NONE, wct, null /* handler */)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ // TODO(b/255649902): ensure remote transition is supplied once state is introduced
+ val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT
+ val handler = remoteTransition?.let {
+ OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
+ }
+ transitions.startTransition(transitionType, wct, handler).also { t ->
+ handler?.setTransition(t)
}
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
}
}
@@ -1093,11 +1098,11 @@ class DesktopTasksController(
controller = null
}
- override fun showDesktopApps(displayId: Int) {
+ override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
"showDesktopApps"
- ) { c -> c.showDesktopApps(displayId) }
+ ) { c -> c.showDesktopApps(displayId, remoteTransition) }
}
override fun stashDesktopApps(displayId: Int) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 47edfd455f5a..6bdaf1eadb8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode;
import android.app.ActivityManager.RunningTaskInfo;
+import android.window.RemoteTransition;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;
/**
@@ -25,7 +26,7 @@ import com.android.wm.shell.desktopmode.IDesktopTaskListener;
interface IDesktopMode {
/** Show apps on the desktop on the given display */
- void showDesktopApps(int displayId);
+ void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
/** Stash apps on the desktop to allow launching another app from home screen */
void stashDesktopApps(int displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 6ea6516a96f5..72fc8686f648 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -30,7 +30,7 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCR
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
import android.window.StartingWindowInfo;
@@ -52,7 +52,8 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
- final boolean allowIcon = (parameter & TYPE_PARAMETER_ALLOW_ICON) != 0;
+ final boolean isSolidColorSplashScreen =
+ (parameter & TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN) != 0;
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
@@ -66,13 +67,13 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
+ "processRunning=%b, "
+ "allowTaskSnapshot=%b, "
+ "activityCreated=%b, "
- + "allowIcon=%b, "
+ + "isSolidColorSplashScreen=%b, "
+ "legacySplashScreen=%b, "
+ "activityDrawn=%b, "
+ "windowless=%b, "
+ "topIsHome=%b",
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
- allowIcon, legacySplashScreen, activityDrawn, windowlessSurface,
+ isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
topIsHome);
if (windowlessSurface) {
@@ -80,7 +81,7 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
}
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
- return getSplashscreenType(allowIcon, legacySplashScreen);
+ return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
}
}
@@ -94,18 +95,18 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
}
}
if (!activityDrawn && !topIsHome) {
- return getSplashscreenType(allowIcon, legacySplashScreen);
+ return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
}
}
return STARTING_WINDOW_TYPE_NONE;
}
- private static int getSplashscreenType(boolean allowIcon, boolean legacySplashScreen) {
- if (allowIcon) {
- return legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ private static int getSplashscreenType(boolean solidColorSplashScreen,
+ boolean legacySplashScreen) {
+ return solidColorSplashScreen
+ ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN
+ : legacySplashScreen
+ ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
: STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else {
- return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
- }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 0d77a2e4610c..ef8393c3b5b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -29,12 +29,16 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.concurrent.Executor;
/**
@@ -74,6 +78,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final TaskViewTaskController mTaskViewTaskController;
private Region mObscuredTouchRegion;
private Insets mCaptionInsets;
+ private Handler mHandler;
public TaskView(Context context, TaskViewTaskController taskViewTaskController) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
@@ -81,6 +86,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
// TODO(b/266736992): Think about a better way to set the TaskViewBase on the
// TaskViewTaskController and vice-versa
mTaskViewTaskController.setTaskViewBase(this);
+ mHandler = Handler.getMain();
getHolder().addCallback(this);
}
@@ -117,14 +123,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
onLocationChanged();
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ final int bgColor = taskInfo.taskDescription.getBackgroundColor();
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
}
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ final int bgColor = taskInfo.taskDescription.getBackgroundColor();
+ runOnViewThread(() -> setResizeBackgroundColor(bgColor));
}
}
@@ -143,7 +151,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
- setResizeBackgroundColor(t, bgColor);
+ runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
}
/**
@@ -272,12 +280,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mHandler = getHandler();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ mHandler = Handler.getMain();
}
/** Returns the task info for the task in the TaskView. */
@@ -285,4 +295,24 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
public ActivityManager.RunningTaskInfo getTaskInfo() {
return mTaskViewTaskController.getTaskInfo();
}
+
+ /**
+ * Sets the handler, only for testing.
+ */
+ @VisibleForTesting
+ void setHandler(Handler viewHandler) {
+ mHandler = viewHandler;
+ }
+
+ /**
+ * Ensures that the given runnable runs on the view's thread.
+ */
+ private void runOnViewThread(Runnable r) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ r.run();
+ } else {
+ // If this call is not from the same thread as the view, then post it
+ mHandler.post(r);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index b444bad8322e..b0e847f69cd5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -26,7 +26,7 @@ import org.junit.Test
open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index e2ab989af027..f847e400bebd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -26,7 +26,7 @@ import org.junit.Test
open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 22b81028dd1d..31e52e2f2ae8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 3fb014f9aac9..0870073c5050 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index ea1f9426bb81..1bb6c4596cd7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 8f23a790081d..bb084d6834b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index b0f39e592744..bc5857f06120 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -26,7 +26,7 @@ import org.junit.Test
open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 6ce874641c6f..e4faa4a9116f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -26,7 +26,7 @@ import org.junit.Test
open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 9f74edf0a79a..7431974951a7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 1e4055e2a50b..c2b060975604 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index c3b8132f4a50..7d1072dc19c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 7756d048efbb..79760d1f23d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index c72aa5aaeca6..ff3729ab96c0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index cc88f27b9045..ffbcfe43f374 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 87b38b133357..5e937c093eb7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index ca347ed25f08..89db8a0c7a76 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index 65978192e8e1..bf4ee7a9bb84 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 6df31fc9419b..7e96f066c1c1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 02596c5f6202..a84deac7698b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 9d579f60eaaa..afcc743e650d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index da853420a705..2f96f5f99a66 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index 1ae2c9eb8a1a..efbeb769508f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index b1b562577acc..232fccc12b1c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index 08c437eda71c..d7f2845553f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index efbf86d028ee..019c946068dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index f7072fae2e7d..567886103c61 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index d80d1120017e..b3c4ea2c7c65 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavLandscape :
SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index 30ec37a41dde..f88180f40196 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -27,7 +27,7 @@ import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavPortrait :
SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 1e086d2f5a9b..391cb9b7c90c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -28,7 +28,7 @@ import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index 932f89217277..4af133ddaa21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -28,7 +28,7 @@ import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
@get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFaasFailure = false)
+ val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
@PlatinumTest(focusArea = "sysui")
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
new file mode 100644
index 000000000000..0df42b3cc5e4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.TransitionInfo;
+import android.window.WindowContainerTransaction;
+
+/**
+ * {@link IRemoteTransition} for testing purposes.
+ * Stores info about
+ * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
+ * IRemoteTransitionFinishedCallback)} being called.
+ */
+public class TestRemoteTransition extends IRemoteTransition.Stub {
+ private boolean mCalled = false;
+ final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
+
+ @Override
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
+ mCalled = true;
+ finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ }
+
+ /**
+ * Check whether this remote transition
+ * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
+ * IRemoteTransitionFinishedCallback)} is called
+ */
+ public boolean isCalled() {
+ return mCalled;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index c6cccc059e89..f2c29f4a623e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -28,10 +28,10 @@ import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
-import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DisplayAreaInfo
+import android.window.RemoteTransition
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
@@ -43,6 +43,7 @@ import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRemoteTransition
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
@@ -57,8 +58,10 @@ import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -69,6 +72,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isA
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
import org.mockito.Mockito
@@ -174,9 +178,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskHidden(task1)
markTaskHidden(task2)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -192,9 +197,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskVisible(task1)
markTaskVisible(task2)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -210,9 +216,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskHidden(task1)
markTaskVisible(task2)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(3)
// Expect order to be from bottom: home, task1, task2
wct.assertReorderAt(index = 0, homeTask)
@@ -224,9 +231,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
val homeTask = setUpHomeTask()
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(1)
wct.assertReorderAt(index = 0, homeTask)
}
@@ -240,9 +248,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
markTaskHidden(taskDefaultDisplay)
markTaskHidden(taskSecondDisplay)
- controller.showDesktopApps(DEFAULT_DISPLAY)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
- val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
assertThat(wct.hierarchyOps).hasSize(2)
// Expect order to be from bottom: home, task
wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
@@ -373,7 +382,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToFullscreen(task)
- val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+ val wct = getLatestWct(type = TRANSIT_CHANGE)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED)
}
@@ -383,7 +392,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val task = setUpFreeformTask()
task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
controller.moveToFullscreen(task)
- val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+ val wct = getLatestWct(type = TRANSIT_CHANGE)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@@ -401,7 +410,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToFullscreen(taskDefaultDisplay)
- with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
}
@@ -414,7 +423,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveTaskToFront(task1)
- val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT)
+ val wct = getLatestWct(type = TRANSIT_TO_FRONT)
assertThat(wct.hierarchyOps).hasSize(1)
wct.assertReorderAt(index = 0, task1)
}
@@ -439,7 +448,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
assertThat(hierarchyOps).hasSize(1)
assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
assertThat(hierarchyOps[0].isReparent).isTrue()
@@ -461,7 +470,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
controller.moveToNextDisplay(task.taskId)
- with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+ with(getLatestWct(type = TRANSIT_CHANGE)) {
assertThat(hierarchyOps).hasSize(1)
assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
assertThat(hierarchyOps[0].isReparent).isTrue()
@@ -747,11 +756,16 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
private fun getLatestWct(
- @WindowManager.TransitionType expectTransition: Int = TRANSIT_OPEN
+ @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+ handlerClass: Class<out TransitionHandler>? = null
): WindowContainerTransaction {
val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
if (ENABLE_SHELL_TRANSITIONS) {
- verify(transitions).startTransition(eq(expectTransition), arg.capture(), isNull())
+ if (handlerClass == null) {
+ verify(transitions).startTransition(eq(type), arg.capture(), isNull())
+ } else {
+ verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
+ }
} else {
verify(shellTaskOrganizer).applyTransaction(arg.capture())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 5efd9ad97a3e..d542139ff9fd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -47,11 +47,8 @@ import static org.mockito.Mockito.spy;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.os.IBinder;
-import android.os.RemoteException;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
-import android.window.IRemoteTransition;
-import android.window.IRemoteTransitionFinishedCallback;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -65,6 +62,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRemoteTransition;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TransitionInfoBuilder;
import com.android.wm.shell.common.DisplayController;
@@ -205,7 +203,7 @@ public class SplitTransitionTests extends ShellTestCase {
// Make sure split-screen is now visible
assertTrue(mStageCoordinator.isSplitScreenVisible());
- assertTrue(testRemote.mCalled);
+ assertTrue(testRemote.isCalled());
}
@Test
@@ -468,24 +466,4 @@ public class SplitTransitionTests extends ShellTestCase {
return out;
}
- class TestRemoteTransition extends IRemoteTransition.Stub {
- boolean mCalled = false;
- final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
-
- @Override
- public void startAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction startTransaction,
- IRemoteTransitionFinishedCallback finishCallback)
- throws RemoteException {
- mCalled = true;
- finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
- }
-
- @Override
- public void mergeAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
- }
- }
-
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0088051928fb..4afb29ecd98c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -43,6 +43,8 @@ import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
@@ -88,6 +90,10 @@ public class TaskViewTest extends ShellTestCase {
SyncTransactionQueue mSyncQueue;
@Mock
Transitions mTransitions;
+ @Mock
+ Looper mViewLooper;
+ @Mock
+ Handler mViewHandler;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -105,6 +111,8 @@ public class TaskViewTest extends ShellTestCase {
.build();
mContext = getContext();
+ doReturn(true).when(mViewLooper).isCurrentThread();
+ doReturn(mViewLooper).when(mViewHandler).getLooper();
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.token = mToken;
@@ -132,6 +140,7 @@ public class TaskViewTest extends ShellTestCase {
mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
mTaskViewTransitions, mSyncQueue));
mTaskView = new TaskView(mContext, mTaskViewTaskController);
+ mTaskView.setHandler(mViewHandler);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -646,4 +655,17 @@ public class TaskViewTest extends ShellTestCase {
assertThat(mTaskViewTaskController.getTaskInfo()).isNull();
}
+
+ @Test
+ public void testOnTaskInfoChangedOnSameUiThread() {
+ mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
+ verify(mViewHandler, never()).post(any());
+ }
+
+ @Test
+ public void testOnTaskInfoChangedOnDifferentUiThread() {
+ doReturn(false).when(mViewLooper).isCurrentThread();
+ mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
+ verify(mViewHandler).post(any());
+ }
}
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 764d1efcc8f4..69fda34afc78 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -166,11 +166,12 @@ public:
#endif
mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
- mBounds)
+ SkSpan<SkRuntimeEffect::ChildPtr>(), mBounds)
.mesh;
} else {
mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
- mBuilder->fUniforms, mBounds)
+ mBuilder->fUniforms, SkSpan<SkRuntimeEffect::ChildPtr>(),
+ mBounds)
.mesh;
}
mIsDirty = false;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 71f47e92e055..ff0d8d74831c 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -539,7 +539,7 @@ struct DrawSkMesh final : Op {
if (!cpuMesh.indexBuffer()) {
gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(),
cpuMesh.vertexOffset(), cpuMesh.refUniforms(),
- cpuMesh.bounds())
+ SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
.mesh;
} else {
sk_sp<SkMesh::IndexBuffer> ib =
@@ -547,7 +547,8 @@ struct DrawSkMesh final : Op {
gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb,
cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib,
cpuMesh.indexCount(), cpuMesh.indexOffset(),
- cpuMesh.refUniforms(), cpuMesh.bounds())
+ cpuMesh.refUniforms(),
+ SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
.mesh;
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index d1ebe6d9f576..1c3399a6650d 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -72,6 +72,7 @@ void HintSessionWrapper::destroy() {
mSessionValid = true;
mHintSession = nullptr;
}
+ mResetsSinceLastReport = 0;
}
bool HintSessionWrapper::init() {
@@ -109,12 +110,13 @@ bool HintSessionWrapper::init() {
tids.push_back(mUiThreadId);
tids.push_back(mRenderThreadId);
- // Use a placeholder target value to initialize,
- // this will always be replaced elsewhere before it gets used
- int64_t defaultTargetDurationNanos = 16666667;
+ // Use the cached target value if there is one, otherwise use a default. This is to ensure
+ // the cached target and target in PowerHAL are consistent, and that it updates correctly
+ // whenever there is a change.
+ int64_t targetDurationNanos =
+ mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration;
mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
- return mBinding->createSession(manager, tids.data(), tids.size(),
- defaultTargetDurationNanos);
+ return mBinding->createSession(manager, tids.data(), tids.size(), targetDurationNanos);
});
return false;
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 36e91ea33c75..41891cd80a42 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -65,6 +65,7 @@ private:
static constexpr nsecs_t kResetHintTimeout = 100_ms;
static constexpr int64_t kSanityCheckLowerBound = 100_us;
static constexpr int64_t kSanityCheckUpperBound = 10_s;
+ static constexpr int64_t kDefaultTargetDuration = 16666667;
// Allows easier stub when testing
class HintSessionBinding {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 94ed06c806e5..f76ea063842f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,6 +17,7 @@
#include "RenderThread.h"
#include <GrContextOptions.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <android-base/properties.h>
#include <dlfcn.h>
#include <gl/GrGLInterface.h>
@@ -286,7 +287,7 @@ void RenderThread::requireGlContext() {
auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
auto size = glesVersion ? strlen(glesVersion) : -1;
cacheManager().configureContext(&options, glesVersion, size);
- sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(std::move(glInterface), options));
+ sk_sp<GrDirectContext> grContext(GrDirectContexts::MakeGL(std::move(glInterface), options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
setGrContext(grContext);
}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 4eabfb238f4a..8445032293dd 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -26,6 +26,21 @@ cc_test {
srcs: [
"PointerController_test.cpp",
],
+ sanitize: {
+ hwaddress: true,
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ },
+ },
shared_libs: [
"libandroid_runtime",
"libinputservice",
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 94faf4a65a1c..d9efd3c2fd83 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -148,6 +148,25 @@ void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t dis
latestPointerDisplayId = displayId;
}
+class TestPointerController : public PointerController {
+public:
+ TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
+ sp<PointerControllerPolicyInterface> policy, const sp<Looper>& looper,
+ SpriteController& spriteController)
+ : PointerController(
+ policy, looper, spriteController,
+ /*enabled=*/true,
+ [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+ // Register listener
+ registeredListener = listener;
+ },
+ [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+ // Unregister listener
+ if (registeredListener == listener) registeredListener = nullptr;
+ }) {}
+ ~TestPointerController() override {}
+};
+
class PointerControllerTest : public Test {
protected:
PointerControllerTest();
@@ -159,6 +178,7 @@ protected:
sp<MockPointerControllerPolicyInterface> mPolicy;
std::unique_ptr<MockSpriteController> mSpriteController;
std::shared_ptr<PointerController> mPointerController;
+ sp<android::gui::WindowInfosListener> mRegisteredListener;
private:
void loopThread();
@@ -181,11 +201,12 @@ PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<Moc
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController =
- PointerController::create(mPolicy, mLooper, *mSpriteController, /*enabled=*/true);
+ mPointerController = std::make_unique<TestPointerController>(mRegisteredListener, mPolicy,
+ mLooper, *mSpriteController);
}
PointerControllerTest::~PointerControllerTest() {
+ mPointerController.reset();
mRunning.store(false, std::memory_order_relaxed);
mThread.join();
}
@@ -316,31 +337,16 @@ TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
class PointerControllerWindowInfoListenerTest : public Test {};
-class TestPointerController : public PointerController {
-public:
- TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
- const sp<Looper>& looper, SpriteController& spriteController)
- : PointerController(
- new MockPointerControllerPolicyInterface(), looper, spriteController,
- /*enabled=*/true,
- [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
- // Register listener
- registeredListener = listener;
- },
- [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
- // Unregister listener
- if (registeredListener == listener) registeredListener = nullptr;
- }) {}
-};
-
TEST_F(PointerControllerWindowInfoListenerTest,
doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
sp<Looper> looper = new Looper(false);
auto spriteController = NiceMock<MockSpriteController>(looper);
sp<android::gui::WindowInfosListener> registeredListener;
sp<android::gui::WindowInfosListener> localListenerCopy;
+ sp<MockPointerControllerPolicyInterface> policy = new MockPointerControllerPolicyInterface();
{
- TestPointerController pointerController(registeredListener, looper, spriteController);
+ TestPointerController pointerController(registeredListener, policy, looper,
+ spriteController);
ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
localListenerCopy = registeredListener;
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index b0cdb0554c11..230fb07f9f43 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1167,7 +1167,10 @@ public final class AudioAttributes implements Parcelable {
streamType);
if (attributes != null) {
mUsage = attributes.mUsage;
- mContentType = attributes.mContentType;
+ // on purpose ignoring the content type: stream types are deprecated for
+ // playback, making assumptions about the content type is prone to
+ // interpretation errors for ambiguous types such as STREAM_TTS and STREAM_MUSIC
+ //mContentType = attributes.mContentType;
mFlags = attributes.getAllFlags();
mMuteHapticChannels = attributes.areHapticChannelsMuted();
mIsContentSpatialized = attributes.isContentSpatialized();
@@ -1177,49 +1180,47 @@ public final class AudioAttributes implements Parcelable {
mSource = attributes.mSource;
}
}
- if (mContentType == CONTENT_TYPE_UNKNOWN) {
- switch (streamType) {
- case AudioSystem.STREAM_VOICE_CALL:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- case AudioSystem.STREAM_SYSTEM_ENFORCED:
- mFlags |= FLAG_AUDIBILITY_ENFORCED;
- // intended fall through, attributes in common with STREAM_SYSTEM
- case AudioSystem.STREAM_SYSTEM:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_RING:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_MUSIC:
- mContentType = CONTENT_TYPE_MUSIC;
- break;
- case AudioSystem.STREAM_ALARM:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_NOTIFICATION:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_BLUETOOTH_SCO:
- mContentType = CONTENT_TYPE_SPEECH;
- mFlags |= FLAG_SCO;
- break;
- case AudioSystem.STREAM_DTMF:
- mContentType = CONTENT_TYPE_SONIFICATION;
- break;
- case AudioSystem.STREAM_TTS:
- mContentType = CONTENT_TYPE_SONIFICATION;
- mFlags |= FLAG_BEACON;
- break;
- case AudioSystem.STREAM_ACCESSIBILITY:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- case AudioSystem.STREAM_ASSISTANT:
- mContentType = CONTENT_TYPE_SPEECH;
- break;
- default:
- Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
- }
+ switch (streamType) {
+ case AudioSystem.STREAM_VOICE_CALL:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_SYSTEM_ENFORCED:
+ mFlags |= FLAG_AUDIBILITY_ENFORCED;
+ // intended fall through, attributes in common with STREAM_SYSTEM
+ case AudioSystem.STREAM_SYSTEM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_RING:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_ALARM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_NOTIFICATION:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mFlags |= FLAG_SCO;
+ break;
+ case AudioSystem.STREAM_DTMF:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_TTS:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mFlags |= FLAG_BEACON;
+ break;
+ case AudioSystem.STREAM_ACCESSIBILITY:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_ASSISTANT:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
+ case AudioSystem.STREAM_MUSIC:
+ // leaving as CONTENT_TYPE_UNKNOWN
+ break;
+ default:
+ Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
}
if (mUsage == USAGE_UNKNOWN) {
mUsage = usageForStreamType(streamType);
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9924fae26194..09f09b94ac0d 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -2409,13 +2409,16 @@ public class Tuner implements AutoCloseable {
@RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
@Nullable
public Descrambler openDescrambler() {
+ acquireTRMSLock("openDescrambler()");
mDemuxLock.lock();
try {
- if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
+ // no need to unlock mDemuxLock (so pass null instead) as TRMS lock is already acquired
+ if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, null)) {
return null;
}
return requestDescrambler();
} finally {
+ releaseTRMSLock();
mDemuxLock.unlock();
}
}
diff --git a/mime/Android.bp b/mime/Android.bp
index a3ea65cb2efe..757862b998b4 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -125,6 +124,6 @@ java_genrule {
srcs: [
"java-res/vendor.mime.types",
],
- // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it
- cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
+ // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it
+ cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | (grep ' ' || echo -n '') | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
}
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
index 38d98a9d47f7..0d4af2a60672 100644
--- a/packages/CredentialManager/shared/Android.bp
+++ b/packages/CredentialManager/shared/Android.bp
@@ -12,6 +12,7 @@ android_library {
manifest: "AndroidManifest.xml",
srcs: ["src/**/*.kt"],
static_libs: [
+ "androidx.activity_activity-compose",
"androidx.core_core-ktx",
"androidx.credentials_credentials",
"guava",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
new file mode 100644
index 000000000000..6498ff7017fb
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager
+
+const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index defba8dacb7b..8986e5237f61 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -17,14 +17,25 @@
package com.android.credentialmanager
import android.content.Intent
+import android.content.pm.PackageManager
import android.credentials.ui.RequestInfo
import com.android.credentialmanager.ktx.requestInfo
import com.android.credentialmanager.mapper.toGet
import com.android.credentialmanager.mapper.toRequestCancel
+import com.android.credentialmanager.mapper.toRequestClose
import com.android.credentialmanager.model.Request
-fun Intent.parse(): Request {
- this.toRequestCancel()?.let { return it }
+fun Intent.parse(
+ packageManager: PackageManager,
+ previousIntent: Intent? = null,
+): Request {
+ this.toRequestClose(packageManager, previousIntent)?.let { closeRequest ->
+ return closeRequest
+ }
+
+ this.toRequestCancel(packageManager)?.let { cancelRequest ->
+ return cancelRequest
+ }
return when (requestInfo?.type) {
RequestInfo.TYPE_CREATE -> {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
new file mode 100644
index 000000000000..ef083fd46f6a
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.activity
+
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.result.contract.ActivityResultContracts
+
+/**
+ * A custom StartIntentSenderForResult contract implementation that attaches an [ActivityOptions]
+ * that opts in for background activity launch.
+ */
+class StartBalIntentSenderForResultContract :
+ ActivityResultContract<IntentSenderRequest, ActivityResult>() {
+ override fun createIntent(context: Context, input: IntentSenderRequest): Intent {
+ val activityOptionBundle =
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ ).toBundle()
+ return Intent(
+ ActivityResultContracts.StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST
+ ).putExtra(
+ ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE,
+ activityOptionBundle
+ ).putExtra(
+ ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST,
+ input
+ )
+ }
+
+ override fun parseResult(
+ resultCode: Int,
+ intent: Intent?
+ ): ActivityResult = ActivityResult(resultCode, intent)
+} \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index a4c20bfabb35..4533db674da2 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -18,10 +18,12 @@ package com.android.credentialmanager.ktx
import android.content.Intent
import android.credentials.ui.CancelUiRequest
+import android.credentials.ui.Constants
import android.credentials.ui.CreateCredentialProviderData
import android.credentials.ui.GetCredentialProviderData
import android.credentials.ui.ProviderData
import android.credentials.ui.RequestInfo
+import android.os.ResultReceiver
val Intent.cancelUiRequest: CancelUiRequest?
get() = this.extras?.getParcelable(
@@ -46,3 +48,9 @@ val Intent.createCredentialProviderDataList: List<ProviderData>
ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
CreateCredentialProviderData::class.java
) ?: emptyList()
+
+val Intent.resultReceiver: ResultReceiver?
+ get() = this.getParcelableExtra(
+ Constants.EXTRA_RESULT_RECEIVER,
+ ResultReceiver::class.java
+ )
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
index 86a6d23a76a9..555a86f59377 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
@@ -17,13 +17,23 @@
package com.android.credentialmanager.mapper
import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.ktx.appLabel
import com.android.credentialmanager.ktx.cancelUiRequest
import com.android.credentialmanager.model.Request
-fun Intent.toRequestCancel(): Request.Cancel? =
+fun Intent.toRequestCancel(packageManager: PackageManager): Request.Cancel? =
this.cancelUiRequest?.let { cancelUiRequest ->
- Request.Cancel(
- showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
- appPackageName = cancelUiRequest.appPackageName
- )
+ val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+ if (appLabel == null) {
+ Log.d(TAG, "Received UI cancel request with an invalid package name.")
+ null
+ } else {
+ Request.Cancel(
+ showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
+ appName = appLabel
+ )
+ }
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
new file mode 100644
index 000000000000..6de3e7d60bf8
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.mapper
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.model.Request
+
+fun Intent.toRequestClose(
+ packageManager: PackageManager,
+ previousIntent: Intent? = null,
+): Request.Close? {
+ // Close request comes as "Cancel" request from Credential Manager API
+ val currentRequest = toRequestCancel(packageManager = packageManager) ?: return null
+
+ if (currentRequest.showCancellationUi) {
+ // Current request is to Cancel and not to Close
+ return null
+ }
+
+ previousIntent?.let {
+ val previousToken = previousIntent.requestInfo?.token
+ val currentToken = this.requestInfo?.token
+
+ if (previousToken != currentToken) {
+ // Current cancellation is for a different request, don't close the current flow.
+ return null
+ }
+ }
+
+ return Request.Close
+} \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index ed9d56344853..ee45fbb00ba6 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -1,22 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.credentialmanager.mapper
import android.content.Intent
+import android.credentials.ui.Entry
import android.credentials.ui.GetCredentialProviderData
+import androidx.credentials.provider.PasswordCredentialEntry
+import com.android.credentialmanager.factory.fromSlice
import com.android.credentialmanager.ktx.getCredentialProviderDataList
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.ktx.resultReceiver
+import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
-fun Intent.toGet() = Request.Get(
- providers = ImmutableMap.copyOf(
- getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
- ),
- entries = ImmutableList.copyOf(
- getCredentialProviderDataList.map { providerData ->
- check(providerData is GetCredentialProviderData) {
- "Invalid provider data type for GetCredentialRequest"
+fun Intent.toGet(): Request.Get {
+ val credentialEntries = mutableListOf<Pair<String, Entry>>()
+ for (providerData in getCredentialProviderDataList) {
+ if (providerData is GetCredentialProviderData) {
+ for (credentialEntry in providerData.credentialEntries) {
+ credentialEntries.add(
+ Pair(providerData.providerFlattenedComponentName, credentialEntry)
+ )
}
- providerData
- }.flatMap { it.credentialEntries }
+ }
+ }
+
+ val passwordEntries = mutableListOf<Password>()
+ for ((providerId, entry) in credentialEntries) {
+ val slice = fromSlice(entry.slice)
+ if (slice is PasswordCredentialEntry) {
+ passwordEntries.add(
+ Password(
+ providerId = providerId,
+ entry = entry,
+ passwordCredentialEntry = slice
+ )
+ )
+ }
+ }
+
+ return Request.Get(
+ token = requestInfo?.token,
+ resultReceiver = this.resultReceiver,
+ providers = ImmutableMap.copyOf(
+ getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
+ ),
+ passwordEntries = ImmutableList.copyOf(passwordEntries)
)
-) \ No newline at end of file
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
new file mode 100644
index 000000000000..2fe4fd5a12e4
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.model
+
+import android.credentials.ui.Entry
+import androidx.credentials.provider.PasswordCredentialEntry
+
+data class Password(
+ val providerId: String,
+ val entry: Entry,
+ val passwordCredentialEntry: PasswordCredentialEntry,
+)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index bc073105efe1..6011a1c1933a 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,8 +16,9 @@
package com.android.credentialmanager.model
-import android.credentials.ui.Entry
import android.credentials.ui.ProviderData
+import android.os.IBinder
+import android.os.ResultReceiver
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
@@ -25,15 +26,33 @@ import com.google.common.collect.ImmutableMap
* Represents the request made by the CredentialManager API.
*/
sealed class Request {
+
+ /**
+ * Request to close the app without displaying a message to the user and without reporting
+ * anything back to the Credential Manager service.
+ */
+ data object Close : Request()
+
+ /**
+ * Request to close the app, displaying a message to the user.
+ */
data class Cancel(
val showCancellationUi: Boolean,
- val appPackageName: String?
+ val appName: String
) : Request()
+ /**
+ * Request to start the get credentials flow.
+ */
data class Get(
+ val token: IBinder?,
+ val resultReceiver: ResultReceiver?,
val providers: ImmutableMap<String, ProviderData>,
- val entries: ImmutableList<Entry>,
+ val passwordEntries: ImmutableList<Password>,
) : Request()
+ /**
+ * Request to start the create credentials flow.
+ */
data object Create : Request()
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
new file mode 100644
index 000000000000..5ab5ab974720
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.repository
+
+import android.app.Application
+import android.content.Intent
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.parse
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class RequestRepository(
+ private val application: Application,
+) {
+
+ private val _requests = MutableStateFlow<Request?>(null)
+ val requests: StateFlow<Request?> = _requests
+
+ suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
+ val request = intent.parse(
+ packageManager = application.packageManager,
+ previousIntent = previousIntent
+ )
+
+ Log.d(TAG, "Request parsed: $request")
+
+ _requests.value = request
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 943c2b4b3a5b..ba88484518aa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,23 +16,183 @@
package com.android.credentialmanager.autofill
+import android.app.assist.AssistStructure
+import android.content.Context
+import android.credentials.GetCredentialRequest
+import android.credentials.CredentialManager
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.os.Bundle
import android.os.CancellationSignal
+import android.os.OutcomeReceiver
+import android.service.autofill.FillRequest
import android.service.autofill.AutofillService
+import android.service.autofill.FillResponse
import android.service.autofill.FillCallback
-import android.service.autofill.FillRequest
import android.service.autofill.SaveRequest
import android.service.autofill.SaveCallback
+import android.util.Log
+import org.json.JSONObject
+import java.util.concurrent.Executors
class CredentialAutofillService : AutofillService() {
+
+ companion object {
+ private const val TAG = "CredAutofill"
+
+ private const val CRED_HINT_PREFIX = "credential="
+ private const val REQUEST_DATA_KEY = "requestData"
+ private const val CANDIDATE_DATA_KEY = "candidateQueryData"
+ private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
+ private const val CRED_OPTIONS_KEY = "credentialOptions"
+ private const val TYPE_KEY = "type"
+ }
+
+ private val credentialManager: CredentialManager =
+ getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
+
override fun onFillRequest(
request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
+ val context = request.fillContexts
+ val structure = context[context.size - 1].structure
+ val callingPackage = structure.activityComponent.packageName
+ Log.i(TAG, "onFillRequest called for $callingPackage")
+
+ val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
+ if (getCredRequest == null) {
+ callback.onFailure("No credential manager request found")
+ return
+ }
+
+ val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> {
+ override fun onResult(result: GetCandidateCredentialsResponse) {
+ Log.i(TAG, "getCandidateCredentials onResponse")
+ val fillResponse: FillResponse? = convertToFillResponse(result, request)
+ callback.onSuccess(fillResponse)
+ }
+
+ override fun onError(error: GetCandidateCredentialsException) {
+ Log.i(TAG, "getCandidateCredentials onError")
+ callback.onFailure("error received from credential manager ${error.message}")
+ }
+ }
+
+ credentialManager.getCandidateCredentials(
+ getCredRequest,
+ callingPackage,
+ CancellationSignal(),
+ Executors.newSingleThreadExecutor(),
+ outcome
+ )
+ }
+
+ private fun convertToFillResponse(
+ getCredResponse: GetCandidateCredentialsResponse,
+ filLRequest: FillRequest
+ ): FillResponse? {
TODO("Not yet implemented")
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
TODO("Not yet implemented")
}
+
+ private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? {
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ traverseStructure(structure, credentialOptions)
+
+ if (credentialOptions.isNotEmpty()) {
+ return GetCredentialRequest.Builder(Bundle.EMPTY)
+ .setCredentialOptions(credentialOptions)
+ .build()
+ }
+ return null
+ }
+
+ private fun traverseStructure(
+ structure: AssistStructure,
+ cmRequests: MutableList<CredentialOption>
+ ) {
+ val windowNodes: List<AssistStructure.WindowNode> =
+ structure.run {
+ (0 until windowNodeCount).map { getWindowNodeAt(it) }
+ }
+
+ windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
+ traverseNode(windowNode.rootViewNode, cmRequests)
+ }
+ }
+
+ private fun traverseNode(
+ viewNode: AssistStructure.ViewNode?,
+ cmRequests: MutableList<CredentialOption>
+ ) {
+ val options = getCredentialOptionsFromViewNode(viewNode)
+ cmRequests.addAll(options)
+
+ val children: List<AssistStructure.ViewNode>? =
+ viewNode?.run {
+ (0 until childCount).map { getChildAt(it) }
+ }
+
+ children?.forEach { childNode: AssistStructure.ViewNode ->
+ traverseNode(childNode, cmRequests)
+ }
+ }
+
+ private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
+ List<CredentialOption> {
+ // TODO(b/293945193) Replace with isCredential check from viewNode
+ val credentialHints: MutableList<String> = mutableListOf()
+ if (viewNode != null && viewNode.autofillHints != null) {
+ for (hint in viewNode.autofillHints!!) {
+ if (hint.startsWith(CRED_HINT_PREFIX)) {
+ credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
+ }
+ }
+ }
+
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ for (credentialHint in credentialHints) {
+ convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) }
+ }
+ return credentialOptions
+ }
+
+ private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
+ // TODO(b/302000646) Move this logic to jetpack so that is consistent
+ // with building the json
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+
+ val json = JSONObject(jsonString)
+ val options = json.getJSONArray(CRED_OPTIONS_KEY)
+ for (i in 0 until options.length()) {
+ val option = options.getJSONObject(i)
+
+ credentialOptions.add(CredentialOption(
+ option.getString(TYPE_KEY),
+ convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
+ convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
+ option.getBoolean(SYS_PROVIDER_REQ_KEY),
+ ))
+ }
+ return credentialOptions
+ }
+
+ private fun convertJsonToBundle(json: JSONObject): Bundle {
+ val result = Bundle()
+ json.keys().forEach {
+ val v = json.get(it)
+ when (v) {
+ is String -> result.putString(it, v)
+ is Boolean -> result.putBoolean(it, v)
+ }
+ }
+ return result
+ }
} \ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index c0dff168969d..e5f5cc255733 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -37,6 +37,7 @@ android_app {
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-runtime-compose",
"androidx.lifecycle_lifecycle-viewmodel-compose",
"androidx.wear.compose_compose-foundation",
"androidx.wear.compose_compose-material",
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 90248734ca7f..b480ac30d2cb 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
/*
* Copyright (c) 2023 Google Inc.
@@ -21,25 +22,27 @@
<uses-feature android:name="android.hardware.type.watch" />
- <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
- <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+ <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
<application
- android:allowBackup="true"
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:label="@string/app_name"
- android:supportsRtl="true">
+ android:name=".CredentialSelectorApp"
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:label="@string/app_name"
+ android:supportsRtl="true">
+ <!-- Activity called by GMS has to be exactly:
+ com.android.credentialmanager.CredentialSelectorActivity -->
<activity
- android:name=".ui.CredentialSelectorActivity"
+ android:name=".CredentialSelectorActivity"
+ android:excludeFromRecents="true"
android:exported="true"
- android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
- android:launchMode="singleTop"
android:label="@string/app_name"
- android:excludeFromRecents="true">
- </activity>
- </application>
+ android:launchMode="singleTop"
+ android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+ </application>
</manifest>
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
new file mode 100644
index 000000000000..273d0b120972
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.wear.compose.material.MaterialTheme
+import com.android.credentialmanager.ui.WearApp
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import kotlinx.coroutines.launch
+
+class CredentialSelectorActivity : ComponentActivity() {
+
+ private val viewModel: CredentialSelectorViewModel by viewModels {
+ CredentialSelectorViewModel.Factory
+ }
+
+ @OptIn(ExperimentalHorologistApi::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setTheme(android.R.style.Theme_DeviceDefault)
+
+ // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a
+ // workaround. Once the issue is fixed, remove the "else" bracket and leave only the
+ // contents of the "if" bracket.
+ if (false) {
+ setContent {
+ MaterialTheme {
+ WearApp(
+ viewModel = viewModel,
+ onCloseApp = ::finish,
+ )
+ }
+ }
+ } else {
+ // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.uiState.collect { uiState ->
+ when (uiState) {
+ CredentialSelectorUiState.Idle -> {
+ // Don't display anything, assuming that there should be minimal latency
+ // to parse the Credential Manager intent and define the state of the
+ // app. If latency is big, then a "loading" screen should be displayed
+ // to the user.
+ }
+
+ is CredentialSelectorUiState.Get -> {
+ setContent {
+ MaterialTheme {
+ SinglePasswordScreen(
+ columnState = belowTimeTextPreview(),
+ onCloseApp = ::finish,
+ )
+ }
+ }
+ }
+
+ else -> finish()
+ }
+ }
+ }
+ }
+ }
+
+ viewModel.onNewIntent(intent)
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+
+ val previousIntent = getIntent()
+ setIntent(intent)
+
+ viewModel.onNewIntent(intent, previousIntent)
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
new file mode 100644
index 000000000000..7c81fd06c4e2
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager
+
+import android.app.Application
+import com.android.credentialmanager.di.inject
+import com.android.credentialmanager.repository.RequestRepository
+
+class CredentialSelectorApp : Application() {
+
+ lateinit var requestRepository: RequestRepository
+
+ override fun onCreate() {
+ super.onCreate()
+
+ inject()
+ }
+} \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
new file mode 100644
index 000000000000..d557dc071db7
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager
+
+import android.content.Intent
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.mappers.toGet
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class CredentialSelectorViewModel(
+ private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+ val uiState: StateFlow<CredentialSelectorUiState> = requestRepository.requests
+ .map { request ->
+ when (request) {
+ null -> CredentialSelectorUiState.Idle
+ is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
+ Request.Close -> CredentialSelectorUiState.Close
+ Request.Create -> CredentialSelectorUiState.Create
+ is Request.Get -> request.toGet()
+ }
+ }
+ .stateIn(
+ viewModelScope,
+ started = SharingStarted.WhileSubscribed(5000),
+ initialValue = CredentialSelectorUiState.Idle,
+ )
+
+ fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
+ viewModelScope.launch {
+ requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
+ }
+ }
+
+ companion object {
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(
+ modelClass: Class<T>,
+ extras: CreationExtras
+ ): T {
+ val application = checkNotNull(extras[APPLICATION_KEY])
+
+ return CredentialSelectorViewModel(
+ requestRepository = (application as CredentialSelectorApp).requestRepository,
+ ) as T
+ }
+ }
+ }
+}
+
+sealed class CredentialSelectorUiState {
+ data object Idle : CredentialSelectorUiState()
+ sealed class Get : CredentialSelectorUiState() {
+ data object SingleProviderSinglePasskey : Get()
+ data object SingleProviderSinglePassword : Get()
+
+ // TODO: b/301206470 add the remaining states
+ }
+
+ data object Create : CredentialSelectorUiState()
+ data class Cancel(val appName: String) : CredentialSelectorUiState()
+ data object Close : CredentialSelectorUiState()
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
new file mode 100644
index 000000000000..a11017b919bf
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
@@ -0,0 +1,17 @@
+package com.android.credentialmanager.di
+
+import android.app.Application
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.repository.RequestRepository
+
+// TODO b/301601582 add Hilt for dependency injection
+
+fun CredentialSelectorApp.inject() {
+ requestRepository = requestRepository(application = this)
+}
+
+private fun requestRepository(
+ application: Application,
+): RequestRepository = RequestRepository(
+ application = application,
+)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
deleted file mode 100644
index 53122ba7ccdc..000000000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0N
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui
-
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.wear.compose.material.MaterialTheme
-import kotlinx.coroutines.launch
-
-class CredentialSelectorActivity : ComponentActivity() {
-
- private val viewModel: CredentialSelectorViewModel by viewModels()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setTheme(android.R.style.Theme_DeviceDefault)
-
- lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.uiState.collect { uiState ->
- when (uiState) {
- CredentialSelectorUiState.Idle -> {
- // Don't display anything, assuming that there should be minimal latency
- // to parse the Credential Manager intent and define the state of the
- // app. If latency is big, then a "loading" screen should be displayed
- // to the user.
- }
-
- is CredentialSelectorUiState.Get -> {
- setContent {
- MaterialTheme {
- WearApp()
- }
- }
- }
-
- CredentialSelectorUiState.Create -> {
- // TODO: b/301206624 - Implement create flow
- finish()
- }
-
- is CredentialSelectorUiState.Cancel -> {
- // TODO: b/300422310 - Implement cancel with message flow
- finish()
- }
-
- CredentialSelectorUiState.Finish -> {
- finish()
- }
- }
- }
- }
- }
-
- viewModel.onNewIntent(intent)
- }
-
- override fun onNewIntent(intent: Intent) {
- super.onNewIntent(intent)
-
- val previousIntent = getIntent()
- setIntent(intent)
-
- viewModel.onNewIntent(intent, previousIntent)
- }
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
deleted file mode 100644
index d22d5d1a28a3..000000000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0N
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.ui
-
-import android.app.Application
-import android.content.Intent
-import android.util.Log
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.parse
-import com.android.credentialmanager.ktx.appLabel
-import com.android.credentialmanager.ktx.requestInfo
-import com.android.credentialmanager.mapper.toGet
-import com.android.credentialmanager.ui.model.PasskeyUiModel
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.ui.mapper.toGet
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-
-class CredentialSelectorViewModel(
- private val application: Application
-) : AndroidViewModel(application = application) {
-
- private val _uiState =
- MutableStateFlow<CredentialSelectorUiState>(CredentialSelectorUiState.Idle)
- val uiState: StateFlow<CredentialSelectorUiState> = _uiState
-
- fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
- viewModelScope.launch {
- val request = intent.parse()
- if (shouldFinishActivity(request = request, previousIntent = previousIntent)) {
- _uiState.value = CredentialSelectorUiState.Finish
- } else {
- when (request) {
- is Request.Cancel -> {
- request.appPackageName?.let { appPackageName ->
- application.packageManager.appLabel(appPackageName)?.let { appLabel ->
- _uiState.value = CredentialSelectorUiState.Cancel(appLabel)
- } ?: run {
- Log.d(TAG,
- "Received UI cancel request with an invalid package name.")
- _uiState.value = CredentialSelectorUiState.Finish
- }
- } ?: run {
- Log.d(TAG, "Received UI cancel request with an invalid package name.")
- _uiState.value = CredentialSelectorUiState.Finish
- }
- }
-
- Request.Create -> {
- _uiState.value = CredentialSelectorUiState.Create
- }
-
- is Request.Get -> {
- _uiState.value = request.toGet()
- }
- }
- }
- }
- }
-
- /**
- * Check if backend requested the UI activity to be cancelled. Different from the other
- * finishing flows, this one does not report anything back to the Credential Manager service
- * backend.
- */
- private fun shouldFinishActivity(request: Request, previousIntent: Intent? = null): Boolean {
- if (request !is Request.Cancel) {
- return false
- } else {
- Log.d(
- TAG, "Received UI cancellation intent. Should show cancellation" +
- " ui = ${request.showCancellationUi}")
-
- previousIntent?.let {
- val previousUiRequest = previousIntent.parse()
-
- if (previousUiRequest is Request.Cancel) {
- val previousToken = previousIntent.requestInfo?.token
- val currentToken = previousIntent.requestInfo?.token
-
- if (previousToken != currentToken) {
- // Cancellation was for a different request, don't cancel the current UI.
- return false
- }
- }
- }
-
- return !request.showCancellationUi
- }
- }
-}
-
-sealed class CredentialSelectorUiState {
- data object Idle : CredentialSelectorUiState()
- sealed class Get : CredentialSelectorUiState() {
- data class SingleProviderSinglePasskey(val passkeyUiModel: PasskeyUiModel) : Get()
- data class SingleProviderSinglePassword(val passwordUiModel: PasswordUiModel) : Get()
-
- // TODO: b/301206470 add the remaining states
- }
-
- data object Create : CredentialSelectorUiState()
- data class Cancel(val appName: String) : CredentialSelectorUiState()
- data object Finish : CredentialSelectorUiState()
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
new file mode 100644
index 000000000000..da5697dab8d4
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.ui
+
+import androidx.navigation.NavController
+
+fun NavController.navigateToLoading() {
+ navigate(Screen.Loading.route)
+}
+
+fun NavController.navigateToSinglePasswordScreen() {
+ navigate(Screen.SinglePasswordScreen.route)
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
index 7d1a49b07718..c3919a03316f 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -19,5 +19,7 @@ package com.android.credentialmanager.ui
sealed class Screen(
val route: String,
) {
- data object Main : Screen("main")
+ data object Loading : Screen("loading")
+
+ data object SinglePasswordScreen : Screen("singlePasswordScreen")
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 19ea9ede9d98..7e0ea3077559 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -19,28 +19,94 @@
package com.android.credentialmanager.ui
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
-import com.android.credentialmanager.ui.screens.MainScreen
+import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorViewModel
+import com.android.credentialmanager.ui.screens.LoadingScreen
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.navscaffold.WearNavScaffold
import com.google.android.horologist.compose.navscaffold.composable
+import com.google.android.horologist.compose.navscaffold.scrollable
@Composable
-fun WearApp() {
+fun WearApp(
+ viewModel: CredentialSelectorViewModel,
+ onCloseApp: () -> Unit,
+) {
val navController = rememberSwipeDismissableNavController()
val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
val navHostState =
rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
WearNavScaffold(
- startDestination = Screen.Main.route,
+ startDestination = Screen.Loading.route,
navController = navController,
state = navHostState,
) {
- composable(Screen.Main.route) {
- MainScreen()
+ composable(Screen.Loading.route) {
+ LoadingScreen()
+ }
+
+ scrollable(Screen.SinglePasswordScreen.route) {
+ SinglePasswordScreen(
+ columnState = it.columnState,
+ onCloseApp = onCloseApp,
+ )
+ }
+ }
+
+ when (val state = uiState) {
+ CredentialSelectorUiState.Idle -> {
+ if (navController.currentDestination?.route != Screen.Loading.route) {
+ navController.navigateToLoading()
+ }
+ }
+
+ is CredentialSelectorUiState.Get -> {
+ handleGetNavigation(
+ navController = navController,
+ state = state,
+ onCloseApp = onCloseApp,
+ )
+ }
+
+ CredentialSelectorUiState.Create -> {
+ // TODO: b/301206624 - Implement create flow
+ onCloseApp()
+ }
+
+ is CredentialSelectorUiState.Cancel -> {
+ // TODO: b/300422310 - Implement cancel with message flow
+ onCloseApp()
+ }
+
+ CredentialSelectorUiState.Close -> {
+ onCloseApp()
+ }
+ }
+}
+
+private fun handleGetNavigation(
+ navController: NavController,
+ state: CredentialSelectorUiState.Get,
+ onCloseApp: () -> Unit,
+) {
+ when (state) {
+ is CredentialSelectorUiState.Get.SingleProviderSinglePassword -> {
+ navController.navigateToSinglePasswordScreen()
+ }
+
+ else -> {
+ // TODO: b/301206470 - Implement other get flows
+ onCloseApp()
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
deleted file mode 100644
index 5ceec1783c84..000000000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.credentialmanager.ui.mapper
-
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
-import com.android.credentialmanager.ui.CredentialSelectorUiState
-import com.android.credentialmanager.factory.fromSlice
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-
-fun Request.Get.toGet(): CredentialSelectorUiState.Get {
- if (this.providers.isEmpty()) {
- throw IllegalStateException("Invalid GetCredential request with empty list of providers.")
- }
-
- if (this.entries.isEmpty()) {
- throw IllegalStateException("Invalid GetCredential request with empty list of entries.")
- }
-
- if (this.providers.size == 1) {
- if (this.entries.size == 1) {
- val slice = this.entries.first().slice
- when (val credentialEntry = fromSlice(slice)) {
- is PasswordCredentialEntry -> {
- return CredentialSelectorUiState.Get.SingleProviderSinglePassword(
- PasswordUiModel(credentialEntry.displayName.toString())
- )
- }
-
- is PublicKeyCredentialEntry -> {
- TODO("b/301206470 - to be implemented")
- }
-
- is CustomCredentialEntry -> {
- TODO("b/301206470 - to be implemented")
- }
-
- else -> {
- throw IllegalStateException(
- "Encountered unrecognized credential entry (${slice.spec?.type}) for " +
- "GetCredential request with single account"
- )
- }
- }
- } else {
- TODO("b/301206470 - to be implemented")
- }
- } else {
- TODO("b/301206470 - to be implemented")
- }
-} \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
new file mode 100644
index 000000000000..f2f878e8ac2f
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.ui.mappers
+
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.CredentialSelectorUiState
+
+fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+ // TODO: b/301206470 returning a hard coded state for MVP
+ if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
+
+ return if (providers.size == 1) {
+ if (passwordEntries.size == 1) {
+ CredentialSelectorUiState.Get.SingleProviderSinglePassword
+ } else {
+ TODO() // b/301206470 - Implement other get flows
+ }
+ } else {
+ TODO() // b/301206470 - Implement other get flows
+ }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
index 94a671efc393..b3ab0c4212db 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
@@ -16,17 +16,15 @@
package com.android.credentialmanager.ui.screens
-import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.wear.compose.material.Text
@Composable
-fun MainScreen(
+fun LoadingScreen(
modifier: Modifier = Modifier
) {
- Box(modifier = modifier, contentAlignment = Alignment.Center) {
- Text("This is a placeholder for the main screen.")
- }
+ // Don't display anything, assuming that there should be minimal latency
+ // to parse the Credential Manager intent and define the state of the
+ // app. If latency is big, then a "loading" screen should be displayed
+ // to the user.
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
index f344ad0bd22d..85327833429d 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalHorologistApi::class)
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
index c8f871e46c83..c9b0230e74b9 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalHorologistApi::class)
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single.passkey
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
@@ -27,6 +27,7 @@ import com.android.credentialmanager.R
import com.android.credentialmanager.ui.components.AccountRow
import com.android.credentialmanager.ui.components.DialogButtonsRow
import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
import com.google.android.horologist.compose.layout.belowTimeTextPreview
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index d863d3c68ceb..c885ec47024a 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -16,17 +16,26 @@
@file:OptIn(ExperimentalHorologistApi::class)
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single.password
+import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.credentialmanager.R
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
import com.android.credentialmanager.ui.components.DialogButtonsRow
import com.android.credentialmanager.ui.components.PasswordRow
import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
import com.google.android.horologist.compose.layout.belowTimeTextPreview
@@ -34,6 +43,61 @@ import com.google.android.horologist.compose.tools.WearPreview
@Composable
fun SinglePasswordScreen(
+ columnState: ScalingLazyColumnState,
+ onCloseApp: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: SinglePasswordScreenViewModel =
+ viewModel(factory = SinglePasswordScreenViewModel.Factory),
+) {
+ viewModel.initialize()
+
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
+ when (val state = uiState) {
+ SinglePasswordScreenUiState.Idle -> {
+ // TODO: b/301206470 implement latency version of the screen
+ }
+
+ is SinglePasswordScreenUiState.Loaded -> {
+ val model = state.passwordUiModel
+ SinglePasswordScreen(
+ email = model.email,
+ onCancelClick = viewModel::onCancelClick,
+ onOKClick = viewModel::onOKClick,
+ columnState = columnState,
+ modifier = modifier
+ )
+ }
+
+ is SinglePasswordScreenUiState.PasswordSelected -> {
+ val launcher = rememberLauncherForActivityResult(
+ StartBalIntentSenderForResultContract()
+ ) {
+ viewModel.onPasswordInfoRetrieved(it.resultCode, it.data)
+ }
+
+ SideEffect {
+ launcher.launch(state.intentSenderRequest)
+ }
+ }
+
+ SinglePasswordScreenUiState.Cancel -> {
+ // TODO: b/301206470 implement navigation for when user taps cancel
+ }
+
+ SinglePasswordScreenUiState.Error -> {
+ // TODO: b/301206470 implement navigation for when there is an error to load screen
+ }
+
+ SinglePasswordScreenUiState.Completed -> {
+ Log.d(TAG, "Received signal to finish the activity.")
+ onCloseApp()
+ }
+ }
+}
+
+@Composable
+fun SinglePasswordScreen(
email: String,
onCancelClick: () -> Unit,
onOKClick: () -> Unit,
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
new file mode 100644
index 000000000000..9b0662257ef6
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0N
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.ui.screens.single.password
+
+import android.content.Intent
+import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.ProviderPendingIntentResponse
+import android.credentials.ui.UserSelectionDialogResult
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.annotation.MainThread
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.model.PasswordUiModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+class SinglePasswordScreenViewModel(
+ private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+ private var initializeCalled = false
+
+ private lateinit var requestGet: Request.Get
+ private lateinit var password: Password
+
+ private val _uiState =
+ MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
+ val uiState: StateFlow<SinglePasswordScreenUiState> = _uiState
+
+ @MainThread
+ fun initialize() {
+ if (initializeCalled) return
+ initializeCalled = true
+
+ viewModelScope.launch {
+ val request = requestRepository.requests.first()
+ Log.d(TAG, "request: $request")
+
+ if (request !is Request.Get) {
+ _uiState.value = SinglePasswordScreenUiState.Error
+ } else {
+ requestGet = request
+ if (requestGet.passwordEntries.isEmpty()) {
+ Log.d(TAG, "Empty passwordEntries")
+ _uiState.value = SinglePasswordScreenUiState.Error
+ } else {
+ password = requestGet.passwordEntries.first()
+ _uiState.value = SinglePasswordScreenUiState.Loaded(
+ PasswordUiModel(
+ email = password.passwordCredentialEntry.username.toString(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ fun onCancelClick() {
+ _uiState.value = SinglePasswordScreenUiState.Cancel
+ }
+
+ fun onOKClick() {
+ // TODO: b/301206470 move this code to shared module
+ val entryIntent = password.entry.frameworkExtrasIntent
+ entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, false)
+ val intentSenderRequest = IntentSenderRequest.Builder(
+ pendingIntent = password.passwordCredentialEntry.pendingIntent
+ ).setFillInIntent(entryIntent).build()
+
+ _uiState.value = SinglePasswordScreenUiState.PasswordSelected(
+ intentSenderRequest = intentSenderRequest
+ )
+ }
+
+ fun onPasswordInfoRetrieved(
+ resultCode: Int? = null,
+ resultData: Intent? = null,
+ ) {
+ // TODO: b/301206470 move this code to shared module
+ Log.d(TAG, "credential selected: {provider=${password.providerId}" +
+ ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
+
+ val userSelectionDialogResult = UserSelectionDialogResult(
+ requestGet.token,
+ password.providerId,
+ password.entry.key,
+ password.entry.subkey,
+ if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+ )
+ val resultDataBundle = Bundle()
+ UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
+ requestGet.resultReceiver?.send(
+ BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+ resultDataBundle
+ )
+
+ _uiState.value = SinglePasswordScreenUiState.Completed
+ }
+
+ companion object {
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(
+ modelClass: Class<T>,
+ extras: CreationExtras
+ ): T {
+ val application = checkNotNull(extras[APPLICATION_KEY])
+
+ return SinglePasswordScreenViewModel(
+ requestRepository = (application as CredentialSelectorApp).requestRepository,
+ ) as T
+ }
+ }
+ }
+}
+
+sealed class SinglePasswordScreenUiState {
+ data object Idle : SinglePasswordScreenUiState()
+ data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
+ data class PasswordSelected(
+ val intentSenderRequest: IntentSenderRequest
+ ) : SinglePasswordScreenUiState()
+
+ data object Cancel : SinglePasswordScreenUiState()
+ data object Error : SinglePasswordScreenUiState()
+ data object Completed : SinglePasswordScreenUiState()
+}
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 1a938d6ec37e..a4089c0d8697 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -22,6 +22,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_NEW_TASK;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
@@ -424,6 +425,13 @@ public abstract class Tile implements Parcelable {
}
/**
+ * Returns if this is searchable.
+ */
+ public boolean isSearchable() {
+ return mMetaData == null || mMetaData.getBoolean(META_DATA_PREFERENCE_SEARCHABLE, true);
+ }
+
+ /**
* The type of the tile.
*/
public enum Type {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 33907ece5a7c..d0929e19bc9b 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -250,6 +250,11 @@ public class TileUtils {
public static final String META_DATA_NEW_TASK = "com.android.settings.new_task";
/**
+ * If the entry should be shown in settings search results. Defaults to true.
+ */
+ public static final String META_DATA_PREFERENCE_SEARCHABLE = "com.android.settings.searchable";
+
+ /**
* Build a list of DashboardCategory.
*/
public static List<DashboardCategory> getCategories(Context context,
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f9ea7735681f..49bd9d994088 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -345,6 +345,8 @@
<string name="accessibility_wifi_three_bars">Wifi three bars.</string>
<!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_signal_full">Wifi signal full.</string>
+ <!-- Content description of the WIFI signal when the WIFI is connected using the signal from a different device owned by the user. For accessibility (not shown on the screen) [CHAR LIMIT=NONE] -->
+ <string name="accessibility_wifi_other_device">Connected to your device.</string>
<!-- Content description of the Wi-Fi security type. This message indicates this is an open Wi-Fi (no password needed) [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_security_type_none">Open network</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index ee65ef4e92b6..ce466dfbf19c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -50,6 +50,7 @@ public class AccessibilityContentDescriptions {
};
public static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
+ public static final int WIFI_OTHER_DEVICE_CONNECTION = R.string.accessibility_wifi_other_device;
public static final int NO_CALLING = R.string.accessibility_no_calling;
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index a95da3033eae..32048ca6344c 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -41,7 +41,7 @@
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.settingslib"
+ android:targetPackage="com.android.settingslib.tests.integ"
android:label="Tests for SettingsLib">
</instrumentation>
</manifest>
diff --git a/packages/SettingsLib/tests/integ/AndroidTest.xml b/packages/SettingsLib/tests/integ/AndroidTest.xml
index b5d09475269e..d0aee8822a72 100644
--- a/packages/SettingsLib/tests/integ/AndroidTest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidTest.xml
@@ -21,7 +21,7 @@
<option name="test-suite-tag" value="apct" />
<option name="test-tag" value="SettingsLibTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.settingslib" />
+ <option name="package" value="com.android.settingslib.tests.integ" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
index 4d2b1ae2ade0..21cdc492e4ea 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
@@ -20,6 +20,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
@@ -256,4 +257,26 @@ public class ActivityTileTest {
assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION);
}
+
+ @Test
+ public void isSearchable_noMetadata_isTrue() {
+ final Tile tile = new ActivityTile(null, "category");
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_notSet_isTrue() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_isSet_false() {
+ mActivityInfo.metaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false);
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.isSearchable()).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
index 80f9efb8b5ac..faccf2f15f49 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
@@ -20,6 +20,7 @@ import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
@@ -257,6 +258,28 @@ public class ProviderTileTest {
assertThat(tile.getType()).isEqualTo(Tile.Type.GROUP);
}
+ @Test
+ public void isSearchable_noMetadata_isTrue() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", null);
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_notSet_isTrue() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_isSet_false() {
+ mMetaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false);
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.isSearchable()).isFalse();
+ }
+
@Implements(TileUtils.class)
private static class ShadowTileUtils {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 1c335445bf80..f6f75de1f24e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -143,6 +143,7 @@ public class SecureSettings {
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
@@ -243,6 +244,7 @@ public class SecureSettings {
Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING,
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
- Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED
+ Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED,
+ Settings.Secure.HUB_MODE_TUTORIAL_STATE
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 301fd6f87aa8..8d13f01f1d03 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -212,6 +212,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.VOLUME_HUSH_GESTURE, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(
Secure.ENABLED_NOTIFICATION_LISTENERS,
@@ -389,5 +390,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DND_CONFIGS_MIGRATED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.HUB_MODE_TUTORIAL_STATE, NON_NEGATIVE_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5e7e044947fd..104f3d2b2621 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -235,6 +235,9 @@ filegroup {
srcs: [
/* Status bar fakes */
"tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt",
+ "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt",
"tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b5b873c8231f..9bfc4be0f30d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -634,7 +634,7 @@
<!-- started from MediaProjectionManager -->
<activity
- android:name=".media.MediaProjectionPermissionActivity"
+ android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
android:exported="true"
android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
android:finishOnCloseSystemDialogs="true"
@@ -643,7 +643,7 @@
android:visibleToInstantApps="true"/>
<activity
- android:name=".media.MediaProjectionAppSelectorActivity"
+ android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index af6fa86bacf4..5c2f979ac639 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -131,5 +131,22 @@
}
]
}
+ ],
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test
+ "sysui-screenshot-test": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
]
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
index 0cc259ab7015..a62c9840add1 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
@@ -151,7 +151,7 @@ internal fun Modifier.element(
element.lastAlpha = alpha
}
}
- .testTag(key.name)
+ .testTag(key.testTag)
}
private fun shouldDrawElement(
@@ -167,7 +167,8 @@ private fun shouldDrawElement(
state.fromScene == state.toScene ||
!layoutImpl.isTransitionReady(state) ||
state.fromScene !in element.sceneValues ||
- state.toScene !in element.sceneValues
+ state.toScene !in element.sceneValues ||
+ !isSharedElementEnabled(layoutImpl, state, element.key)
) {
return true
}
@@ -191,6 +192,26 @@ private fun shouldDrawElement(
}
}
+private fun isSharedElementEnabled(
+ layoutImpl: SceneTransitionLayoutImpl,
+ transition: TransitionState.Transition,
+ element: ElementKey,
+): Boolean {
+ val spec = layoutImpl.transitions.transitionSpec(transition.fromScene, transition.toScene)
+ val sharedInFromScene = spec.transformations(element, transition.fromScene).shared
+ val sharedInToScene = spec.transformations(element, transition.toScene).shared
+
+ // The sharedElement() transformation must either be null or be the same in both scenes.
+ if (sharedInFromScene != sharedInToScene) {
+ error(
+ "Different sharedElement() transformations matched $element (from=$sharedInFromScene " +
+ "to=$sharedInToScene)"
+ )
+ }
+
+ return sharedInFromScene?.enabled ?: true
+}
+
/**
* Chain the [com.android.compose.animation.scene.transformation.ModifierTransformation] applied
* throughout the current transition, if any.
@@ -213,7 +234,7 @@ private fun Modifier.modifierTransformations(
return layoutImpl.transitions
.transitionSpec(fromScene, state.toScene)
- .transformations(element.key)
+ .transformations(element.key, scene.key)
.modifier
.fold(this) { modifier, transformation ->
with(transformation) {
@@ -407,17 +428,20 @@ private inline fun <T> computeValue(
// The element is shared: interpolate between the value in fromScene and the value in toScene.
// TODO(b/290184746): Support non linear shared paths as well as a way to make sure that shared
// elements follow the finger direction.
- if (fromValues != null && toValues != null) {
+ val isSharedElement = fromValues != null && toValues != null
+ if (isSharedElement && isSharedElementEnabled(layoutImpl, state, element.key)) {
return lerp(
- sceneValue(fromValues),
- sceneValue(toValues),
+ sceneValue(fromValues!!),
+ sceneValue(toValues!!),
transitionProgress,
)
}
val transformation =
transformation(
- layoutImpl.transitions.transitionSpec(fromScene, toScene).transformations(element.key)
+ layoutImpl.transitions
+ .transitionSpec(fromScene, toScene)
+ .transformations(element.key, scene.key)
)
// If there is no transformation explicitly associated to this element value, let's use
// the value given by the system (like the current position and size given by the layout
@@ -426,12 +450,21 @@ private inline fun <T> computeValue(
// Get the transformed value, i.e. the target value at the beginning (for entering elements) or
// end (for leaving elements) of the transition.
+ val sceneValues =
+ checkNotNull(
+ when {
+ isSharedElement && scene.key == fromScene -> fromValues
+ isSharedElement -> toValues
+ else -> fromValues ?: toValues
+ }
+ )
+
val targetValue =
transformation.transform(
layoutImpl,
scene,
element,
- fromValues ?: toValues!!,
+ sceneValues,
state,
idleValue,
)
@@ -440,7 +473,7 @@ private inline fun <T> computeValue(
val rangeProgress = transformation.range?.progress(transitionProgress) ?: transitionProgress
// Interpolate between the value at rest and the value before entering/after leaving.
- val isEntering = fromValues == null
+ val isEntering = scene.key == toScene
return if (isEntering) {
lerp(targetValue, idleValue, rangeProgress)
} else {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt
new file mode 100644
index 000000000000..98dbb67d7c66
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+/** An interface to match one or more elements. */
+interface ElementMatcher {
+ /** Whether the element with key [key] in scene [scene] matches this matcher. */
+ fun matches(key: ElementKey, scene: SceneKey): Boolean
+}
+
+/**
+ * Returns an [ElementMatcher] that matches elements in [scene] also matching [this]
+ * [ElementMatcher].
+ */
+fun ElementMatcher.inScene(scene: SceneKey): ElementMatcher {
+ val delegate = this
+ val matcherScene = scene
+ return object : ElementMatcher {
+ override fun matches(key: ElementKey, scene: SceneKey): Boolean {
+ return scene == matcherScene && delegate.matches(key, scene)
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
index f7ebe2fc6d34..b7acc48e2865 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
@@ -16,6 +16,8 @@
package com.android.compose.animation.scene
+import androidx.annotation.VisibleForTesting
+
/**
* A base class to create unique keys, associated to an [identity] that is used to check the
* equality of two key instances.
@@ -41,6 +43,7 @@ class SceneKey(
name: String,
identity: Any = Object(),
) : Key(name, identity) {
+ @VisibleForTesting val testTag: String = "scene:$name"
/** The unique [ElementKey] identifying this scene's root element. */
val rootElementKey = ElementKey(name, identity)
@@ -61,7 +64,9 @@ class ElementKey(
*/
val isBackground: Boolean = false,
) : Key(name, identity), ElementMatcher {
- override fun matches(key: ElementKey): Boolean {
+ @VisibleForTesting val testTag: String = "element:$name"
+
+ override fun matches(key: ElementKey, scene: SceneKey): Boolean {
return key == this
}
@@ -73,7 +78,9 @@ class ElementKey(
/** Matches any element whose [key identity][ElementKey.identity] matches [predicate]. */
fun withIdentity(predicate: (Any) -> Boolean): ElementMatcher {
return object : ElementMatcher {
- override fun matches(key: ElementKey): Boolean = predicate(key.identity)
+ override fun matches(key: ElementKey, scene: SceneKey): Boolean {
+ return predicate(key.identity)
+ }
}
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
index b44c8efc7ee2..3985233bd197 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
@@ -25,6 +25,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.zIndex
@@ -45,7 +46,9 @@ internal class Scene(
@Composable
fun Content(modifier: Modifier = Modifier) {
- Box(modifier.zIndex(zIndex).onPlaced { size = it.size }) { scope.content() }
+ Box(modifier.zIndex(zIndex).onPlaced { size = it.size }.testTag(key.testTag)) {
+ scope.content()
+ }
}
override fun toString(): String {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
index f4e39023edfe..75dcb2e44c13 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -16,6 +16,7 @@
package com.android.compose.animation.scene
+import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.snap
import androidx.compose.ui.geometry.Offset
@@ -28,6 +29,7 @@ import com.android.compose.animation.scene.transformation.ModifierTransformation
import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
+import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.Translate
import com.android.compose.ui.util.fastForEach
@@ -35,11 +37,12 @@ import com.android.compose.ui.util.fastMap
/** The transitions configuration of a [SceneTransitionLayout]. */
class SceneTransitions(
- private val transitionSpecs: List<TransitionSpec>,
+ @get:VisibleForTesting val transitionSpecs: List<TransitionSpec>,
) {
private val cache = mutableMapOf<SceneKey, MutableMap<SceneKey, TransitionSpec>>()
- internal fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
+ @VisibleForTesting
+ fun transitionSpec(from: SceneKey, to: SceneKey): TransitionSpec {
return cache.getOrPut(from) { mutableMapOf() }.getOrPut(to) { findSpec(from, to) }
}
@@ -97,7 +100,8 @@ data class TransitionSpec(
val transformations: List<Transformation>,
val spec: AnimationSpec<Float>,
) {
- private val cache = mutableMapOf<ElementKey, ElementTransformations>()
+ // TODO(b/302300957): Make sure this cache does not infinitely grow.
+ private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>()
internal fun reverse(): TransitionSpec {
return copy(
@@ -107,12 +111,18 @@ data class TransitionSpec(
)
}
- internal fun transformations(element: ElementKey): ElementTransformations {
- return cache.getOrPut(element) { computeTransformations(element) }
+ internal fun transformations(element: ElementKey, scene: SceneKey): ElementTransformations {
+ return cache
+ .getOrPut(element) { mutableMapOf() }
+ .getOrPut(scene) { computeTransformations(element, scene) }
}
/** Filter [transformations] to compute the [ElementTransformations] of [element]. */
- private fun computeTransformations(element: ElementKey): ElementTransformations {
+ private fun computeTransformations(
+ element: ElementKey,
+ scene: SceneKey,
+ ): ElementTransformations {
+ var shared: SharedElementTransformation? = null
val modifier = mutableListOf<ModifierTransformation>()
var offset: PropertyTransformation<Offset>? = null
var size: PropertyTransformation<IntSize>? = null
@@ -126,16 +136,16 @@ data class TransitionSpec(
is Translate,
is EdgeTranslate,
is AnchoredTranslate -> {
- throwIfNotNull(offset, element, property = "offset")
+ throwIfNotNull(offset, element, name = "offset")
offset = root as PropertyTransformation<Offset>
}
is ScaleSize,
is AnchoredSize -> {
- throwIfNotNull(size, element, property = "size")
+ throwIfNotNull(size, element, name = "size")
size = root as PropertyTransformation<IntSize>
}
is Fade -> {
- throwIfNotNull(alpha, element, property = "alpha")
+ throwIfNotNull(alpha, element, name = "alpha")
alpha = root as PropertyTransformation<Float>
}
is RangedPropertyTransformation -> onPropertyTransformation(root, current.delegate)
@@ -143,32 +153,37 @@ data class TransitionSpec(
}
transformations.fastForEach { transformation ->
- if (!transformation.matcher.matches(element)) {
+ if (!transformation.matcher.matches(element, scene)) {
return@fastForEach
}
when (transformation) {
+ is SharedElementTransformation -> {
+ throwIfNotNull(shared, element, name = "shared")
+ shared = transformation
+ }
is ModifierTransformation -> modifier.add(transformation)
is PropertyTransformation<*> -> onPropertyTransformation(transformation)
}
}
- return ElementTransformations(modifier, offset, size, alpha)
+ return ElementTransformations(shared, modifier, offset, size, alpha)
}
private fun throwIfNotNull(
- previous: PropertyTransformation<*>?,
+ previous: Transformation?,
element: ElementKey,
- property: String,
+ name: String,
) {
if (previous != null) {
- error("$element has multiple transformations for its $property property")
+ error("$element has multiple $name transformations")
}
}
}
/** The transformations of an element during a transition. */
internal class ElementTransformations(
+ val shared: SharedElementTransformation?,
val modifier: List<ModifierTransformation>,
val offset: PropertyTransformation<Offset>?,
val size: PropertyTransformation<IntSize>?,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
index fb12b90d7d3e..49669775fedd 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -116,6 +116,14 @@ interface TransitionBuilder : PropertyTransformationBuilder {
)
/**
+ * Configure the shared transition when [matcher] is shared between two scenes.
+ *
+ * @param enabled whether the matched element(s) should actually be shared in this transition.
+ * Defaults to true.
+ */
+ fun sharedElement(matcher: ElementMatcher, enabled: Boolean = true)
+
+ /**
* Punch a hole in the element(s) matching [matcher] that has the same bounds as [bounds] and
* using the given [shape].
*
@@ -127,6 +135,13 @@ interface TransitionBuilder : PropertyTransformationBuilder {
* the result.
*/
fun punchHole(matcher: ElementMatcher, bounds: ElementKey, shape: Shape = RectangleShape)
+
+ /**
+ * Adds the transformations in [builder] but in reversed order. This allows you to partially
+ * reuse the definition of the transition from scene `Foo` to scene `Bar` inside the definition
+ * of the transition from scene `Bar` to scene `Foo`.
+ */
+ fun reversed(builder: TransitionBuilder.() -> Unit)
}
@TransitionDsl
@@ -179,12 +194,6 @@ interface PropertyTransformationBuilder {
fun anchoredSize(matcher: ElementMatcher, anchor: ElementKey)
}
-/** An interface to match one or more elements. */
-interface ElementMatcher {
- /** Whether the element with key [key] matches this matcher. */
- fun matches(key: ElementKey): Boolean
-}
-
/** The edge of a [SceneTransitionLayout]. */
enum class Edge {
Left,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 48d5638e8b4e..f1c27178391c 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -31,6 +31,7 @@ import com.android.compose.animation.scene.transformation.PropertyTransformation
import com.android.compose.animation.scene.transformation.PunchHole
import com.android.compose.animation.scene.transformation.RangedPropertyTransformation
import com.android.compose.animation.scene.transformation.ScaleSize
+import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.animation.scene.transformation.Transformation
import com.android.compose.animation.scene.transformation.TransformationRange
import com.android.compose.animation.scene.transformation.Translate
@@ -80,6 +81,7 @@ internal class TransitionBuilderImpl : TransitionBuilder {
override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
private var range: TransformationRange? = null
+ private var reversed = false
private val durationMillis: Int by lazy {
val spec = spec
if (spec !is DurationBasedAnimationSpec) {
@@ -93,6 +95,12 @@ internal class TransitionBuilderImpl : TransitionBuilder {
transformations.add(PunchHole(matcher, bounds, shape))
}
+ override fun reversed(builder: TransitionBuilder.() -> Unit) {
+ reversed = true
+ builder()
+ reversed = false
+ }
+
override fun fractionRange(
start: Float?,
end: Float?,
@@ -103,6 +111,10 @@ internal class TransitionBuilderImpl : TransitionBuilder {
range = null
}
+ override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
+ transformations.add(SharedElementTransformation(matcher, enabled))
+ }
+
override fun timestampRange(
startMillis: Int?,
endMillis: Int?,
@@ -122,11 +134,20 @@ internal class TransitionBuilderImpl : TransitionBuilder {
}
private fun transformation(transformation: PropertyTransformation<*>) {
- if (range != null) {
- transformations.add(RangedPropertyTransformation(transformation, range!!))
- } else {
- transformations.add(transformation)
- }
+ val transformation =
+ if (range != null) {
+ RangedPropertyTransformation(transformation, range!!)
+ } else {
+ transformation
+ }
+
+ transformations.add(
+ if (reversed) {
+ transformation.reverse()
+ } else {
+ transformation
+ }
+ )
}
override fun fade(matcher: ElementMatcher) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
index ce6749da2711..a65025423aee 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -30,6 +30,14 @@ sealed interface Transformation {
*/
val matcher: ElementMatcher
+ /**
+ * The range during which the transformation is applied. If it is `null`, then the
+ * transformation will be applied throughout the whole scene transition.
+ */
+ // TODO(b/240432457): Move this back to PropertyTransformation.
+ val range: TransformationRange?
+ get() = null
+
/*
* Reverse this transformation. This is called when we use Transition(from = A, to = B) when
* animating from B to A and there is no Transition(from = B, to = A) defined.
@@ -37,6 +45,11 @@ sealed interface Transformation {
fun reverse(): Transformation = this
}
+internal class SharedElementTransformation(
+ override val matcher: ElementMatcher,
+ internal val enabled: Boolean,
+) : Transformation
+
/** A transformation that is applied on the element during the whole transition. */
internal interface ModifierTransformation : Transformation {
/** Apply the transformation to [element]. */
@@ -53,13 +66,6 @@ internal interface ModifierTransformation : Transformation {
/** A transformation that changes the value of an element property, like its size or offset. */
internal sealed interface PropertyTransformation<T> : Transformation {
/**
- * The range during which the transformation is applied. If it is `null`, then the
- * transformation will be applied throughout the whole scene transition.
- */
- val range: TransformationRange?
- get() = null
-
- /**
* Transform [value], i.e. the value of the transformed property without this transformation.
*/
// TODO(b/290184746): Figure out a public API for custom transformations that don't have access
@@ -92,8 +98,7 @@ internal class RangedPropertyTransformation<T>(
}
/** The progress-based range of a [PropertyTransformation]. */
-data class TransformationRange
-private constructor(
+data class TransformationRange(
val start: Float,
val end: Float,
) {
@@ -133,6 +138,6 @@ private constructor(
}
companion object {
- private const val BoundUnspecified = Float.MIN_VALUE
+ const val BoundUnspecified = Float.MIN_VALUE
}
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 8bd654585f29..328866ea76ca 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -224,7 +224,7 @@ class SceneTransitionLayoutTest {
// In scene A, the shared element SharedFoo() is at the top end of the layout and has a size
// of 50.dp.
- var sharedFoo = rule.onNodeWithTag(TestElements.Foo.name, useUnmergedTree = true)
+ var sharedFoo = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
sharedFoo.assertWidthIsEqualTo(50.dp)
sharedFoo.assertHeightIsEqualTo(50.dp)
sharedFoo.assertPositionInRootIsEqualTo(
@@ -250,7 +250,7 @@ class SceneTransitionLayoutTest {
// We need to use onAllNodesWithTag().onFirst() here given that shared elements are
// composed and laid out in both scenes (but drawn only in one).
- sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.name).onFirst()
+ sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.testTag).onFirst()
// In scene B, foo is at the top start (x = 0, y = 0) of the layout and has a size of
// 100.dp. We pause at the middle of the transition, so it should now be 75.dp given that we
@@ -284,7 +284,7 @@ class SceneTransitionLayoutTest {
val expectedLeft = 0.dp
val expectedSize = 100.dp + (150.dp - 100.dp) * interpolatedProgress
- sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.name).onFirst()
+ sharedFoo = rule.onAllNodesWithTag(TestElements.Foo.testTag).onFirst()
assertThat((layoutState.transitionState as TransitionState.Transition).progress)
.isEqualTo(interpolatedProgress)
sharedFoo.assertWidthIsEqualTo(expectedSize)
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
index 275149a05abf..268057fd2f2c 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
@@ -23,7 +23,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.SemanticsNodeInteractionCollection
+import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
@DslMarker annotation class TransitionTestDsl
@@ -59,8 +63,21 @@ interface TransitionTestBuilder {
@TransitionTestDsl
interface TransitionTestAssertionScope {
- /** Assert on [element]. */
- fun onElement(element: ElementKey): SemanticsNodeInteraction
+ /**
+ * Assert on [element].
+ *
+ * Note that presence/value assertions on the returned [SemanticsNodeInteraction] will fail if 0
+ * or more than 1 elements matched [element]. If you need to assert on a shared element that
+ * will be present multiple times in the layout during transitions, either specify the [scene]
+ * in which you are matching or use [onSharedElement] instead.
+ */
+ fun onElement(element: ElementKey, scene: SceneKey? = null): SemanticsNodeInteraction
+
+ /**
+ * Assert on a shared [element]. This will throw if [element] is not shared and present only in
+ * one scene during a transition.
+ */
+ fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection
}
/**
@@ -73,20 +90,22 @@ fun ComposeContentTestRule.testTransition(
toSceneContent: @Composable SceneScope.() -> Unit,
transition: TransitionBuilder.() -> Unit,
layoutModifier: Modifier = Modifier,
+ fromScene: SceneKey = TestScenes.SceneA,
+ toScene: SceneKey = TestScenes.SceneB,
builder: TransitionTestBuilder.() -> Unit,
) {
testTransition(
- from = TestScenes.SceneA,
- to = TestScenes.SceneB,
+ from = fromScene,
+ to = toScene,
transitionLayout = { currentScene, onChangeScene ->
SceneTransitionLayout(
currentScene,
onChangeScene,
- transitions { from(TestScenes.SceneA, to = TestScenes.SceneB, transition) },
+ transitions { from(fromScene, to = toScene, transition) },
layoutModifier.fillMaxSize(),
) {
- scene(TestScenes.SceneA, content = fromSceneContent)
- scene(TestScenes.SceneB, content = toSceneContent)
+ scene(fromScene, content = fromSceneContent)
+ scene(toScene, content = toSceneContent)
}
},
builder,
@@ -111,8 +130,24 @@ fun ComposeContentTestRule.testTransition(
val test = transitionTest(builder)
val assertionScope =
object : TransitionTestAssertionScope {
- override fun onElement(element: ElementKey): SemanticsNodeInteraction {
- return this@testTransition.onNodeWithTag(element.name)
+ override fun onElement(
+ element: ElementKey,
+ scene: SceneKey?
+ ): SemanticsNodeInteraction {
+ return if (scene == null) {
+ onNodeWithTag(element.testTag)
+ } else {
+ onNode(hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag)))
+ }
+ }
+
+ override fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection {
+ val interaction = onAllNodesWithTag(element.testTag)
+ val matches = interaction.fetchSemanticsNodes(atLeastOneRootRequired = false).size
+ if (matches < 2) {
+ error("Element $element is not shared ($matches matches)")
+ }
+ return interaction
}
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
new file mode 100644
index 000000000000..fa94b25028a2
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.tween
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.transformation.Transformation
+import com.android.compose.animation.scene.transformation.TransformationRange
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TransitionDslTest {
+ @Test
+ fun emptyTransitions() {
+ val transitions = transitions {}
+ assertThat(transitions.transitionSpecs).isEmpty()
+ }
+
+ @Test
+ fun manyTransitions() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB)
+ from(TestScenes.SceneB, to = TestScenes.SceneC)
+ from(TestScenes.SceneC, to = TestScenes.SceneA)
+ }
+ assertThat(transitions.transitionSpecs).hasSize(3)
+ }
+
+ @Test
+ fun toFromBuilders() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB)
+ from(TestScenes.SceneB)
+ to(TestScenes.SceneC)
+ }
+
+ assertThat(transitions.transitionSpecs)
+ .comparingElementsUsing(
+ Correspondence.transforming<TransitionSpec, Pair<SceneKey?, SceneKey?>>(
+ { it?.from to it?.to },
+ "has (from, to) equal to"
+ )
+ )
+ .containsExactly(
+ TestScenes.SceneA to TestScenes.SceneB,
+ TestScenes.SceneB to null,
+ null to TestScenes.SceneC,
+ )
+ }
+
+ @Test
+ fun defaultTransitionSpec() {
+ val transitions = transitions { from(TestScenes.SceneA, to = TestScenes.SceneB) }
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.spec).isInstanceOf(SpringSpec::class.java)
+ }
+
+ @Test
+ fun customTransitionSpec() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) { spec = tween(durationMillis = 42) }
+ }
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.spec).isInstanceOf(TweenSpec::class.java)
+ assertThat((transition.spec as TweenSpec).durationMillis).isEqualTo(42)
+ }
+
+ @Test
+ fun defaultRange() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) { fade(TestElements.Foo) }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations.size).isEqualTo(1)
+ assertThat(transition.transformations.single().range).isEqualTo(null)
+ }
+
+ @Test
+ fun fractionRange() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ fractionRange(start = 0.2f) { fade(TestElements.Foo) }
+ fractionRange(end = 0.9f) { fade(TestElements.Foo) }
+ }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 0.1f, end = 0.8f),
+ TransformationRange(start = 0.2f, end = TransformationRange.BoundUnspecified),
+ TransformationRange(start = TransformationRange.BoundUnspecified, end = 0.9f),
+ )
+ }
+
+ @Test
+ fun timestampRange() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(500)
+
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 200) { fade(TestElements.Foo) }
+ timestampRange(endMillis = 400) { fade(TestElements.Foo) }
+ }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 100 / 500f, end = 300 / 500f),
+ TransformationRange(start = 200 / 500f, end = TransformationRange.BoundUnspecified),
+ TransformationRange(start = TransformationRange.BoundUnspecified, end = 400 / 500f),
+ )
+ }
+
+ @Test
+ fun reversed() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(500)
+ reversed {
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ }
+ }
+ }
+
+ val transition = transitions.transitionSpecs.single()
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
+ TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f),
+ )
+ }
+
+ @Test
+ fun defaultReversed() {
+ val transitions = transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ spec = tween(500)
+ fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+ timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+ }
+ }
+
+ // Fetch the transition from B to A, which will automatically reverse the transition from A
+ // to B we defined.
+ val transition =
+ transitions.transitionSpec(from = TestScenes.SceneB, to = TestScenes.SceneA)
+ assertThat(transition.transformations)
+ .comparingElementsUsing(TRANSFORMATION_RANGE)
+ .containsExactly(
+ TransformationRange(start = 1f - 0.8f, end = 1f - 0.1f),
+ TransformationRange(start = 1f - 300 / 500f, end = 1f - 100 / 500f),
+ )
+ }
+
+ companion object {
+ private val TRANSFORMATION_RANGE =
+ Correspondence.transforming<Transformation, TransformationRange?>(
+ { it?.range },
+ "has range equal to"
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
new file mode 100644
index 000000000000..2af363860272
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.transformation
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.TestScenes
+import com.android.compose.animation.scene.inScene
+import com.android.compose.animation.scene.testTransition
+import com.android.compose.modifiers.size
+import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.onEach
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SharedElementTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun testSharedElement() {
+ rule.testTransition(
+ fromSceneContent = {
+ // Foo is at (10, 50) with a size of (20, 80).
+ Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo).size(20.dp, 80.dp))
+ },
+ toSceneContent = {
+ // Foo is at (50, 70) with a size of (10, 40).
+ Box(Modifier.offset(50.dp, 70.dp).element(TestElements.Foo).size(10.dp, 40.dp))
+ },
+ transition = {
+ spec = tween(16 * 4, easing = LinearEasing)
+ // Elements should be shared by default.
+ }
+ ) {
+ before {
+ onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ onElement(TestElements.Foo).assertSizeIsEqualTo(20.dp, 80.dp)
+ }
+ at(0) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ assertSizeIsEqualTo(20.dp, 80.dp)
+ }
+ }
+ at(16) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(20.dp, 55.dp)
+ assertSizeIsEqualTo(17.5.dp, 70.dp)
+ }
+ }
+ at(32) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(30.dp, 60.dp)
+ assertSizeIsEqualTo(15.dp, 60.dp)
+ }
+ }
+ at(48) {
+ onSharedElement(TestElements.Foo).onEach {
+ assertPositionInRootIsEqualTo(40.dp, 65.dp)
+ assertSizeIsEqualTo(12.5.dp, 50.dp)
+ }
+ }
+ after {
+ onElement(TestElements.Foo).assertPositionInRootIsEqualTo(50.dp, 70.dp)
+ onElement(TestElements.Foo).assertSizeIsEqualTo(10.dp, 40.dp)
+ }
+ }
+ }
+
+ @Test
+ fun testSharedElementDisabled() {
+ rule.testTransition(
+ fromScene = TestScenes.SceneA,
+ toScene = TestScenes.SceneB,
+ // The full layout is 100x100.
+ layoutModifier = Modifier.size(100.dp),
+ fromSceneContent = {
+ Box(Modifier.fillMaxSize()) {
+ // Foo is at (10, 50).
+ Box(Modifier.offset(10.dp, 50.dp).element(TestElements.Foo))
+ }
+ },
+ toSceneContent = {
+ Box(Modifier.fillMaxSize()) {
+ // Foo is at (50, 60).
+ Box(Modifier.offset(50.dp, 60.dp).element(TestElements.Foo))
+ }
+ },
+ transition = {
+ spec = tween(16 * 4, easing = LinearEasing)
+
+ // Disable the shared element animation.
+ sharedElement(TestElements.Foo, enabled = false)
+
+ // In SceneA, Foo leaves to the left edge.
+ translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left)
+
+ // In SceneB, Foo comes from the bottom edge.
+ translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom)
+ },
+ ) {
+ before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp) }
+ at(0) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(10.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 100.dp)
+ }
+ at(16) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(7.5.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 90.dp)
+ }
+ at(32) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(5.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 80.dp)
+ }
+ at(48) {
+ onElement(TestElements.Foo, scene = TestScenes.SceneA)
+ .assertPositionInRootIsEqualTo(2.5.dp, 50.dp)
+ onElement(TestElements.Foo, scene = TestScenes.SceneB)
+ .assertPositionInRootIsEqualTo(50.dp, 70.dp)
+ }
+ after { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(50.dp, 60.dp) }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt
new file mode 100644
index 000000000000..d6f64bfe4974
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.test
+
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.SemanticsNodeInteractionCollection
+
+/** Assert [assert] on each element of [this] [SemanticsNodeInteractionCollection]. */
+fun SemanticsNodeInteractionCollection.onEach(assert: SemanticsNodeInteraction.() -> Unit) {
+ for (i in 0 until this.fetchSemanticsNodes().size) {
+ get(i).assert()
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 8e3400872a9a..519c0a9c4c7c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -39,7 +39,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
@@ -166,11 +168,18 @@ fun SceneScope.ExpandedShadeHeader(
modifier =
Modifier.align(Alignment.CenterVertically)
// use graphicsLayer instead of Modifier.scale to anchor transform to
- // top left corner
+ // the (start, top) corner
.graphicsLayer(
scaleX = 2.57f,
scaleY = 2.57f,
- transformOrigin = TransformOrigin(0f, 0.5f)
+ transformOrigin =
+ TransformOrigin(
+ when (LocalLayoutDirection.current) {
+ LayoutDirection.Ltr -> 0f
+ LayoutDirection.Rtl -> 1f
+ },
+ 0.5f
+ )
),
)
Spacer(modifier = Modifier.weight(1f))
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index c41dc53fdc6b..cb76ad7c77fe 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -507,9 +507,10 @@ open class ClockRegistry(
}
}
- private var isVerifying = AtomicBoolean(false)
+ private var isQueued = AtomicBoolean(false)
fun verifyLoadedProviders() {
- val shouldSchedule = isVerifying.compareAndSet(false, true)
+ Log.i(TAG, Thread.currentThread().getStackTrace().toString())
+ val shouldSchedule = isQueued.compareAndSet(false, true)
if (!shouldSchedule) {
logger.tryLog(
TAG,
@@ -521,48 +522,54 @@ open class ClockRegistry(
}
scope.launch(bgDispatcher) {
- if (keepAllLoaded) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- {},
- { "verifyLoadedProviders: keepAllLoaded=true" }
- )
- // Enforce that all plugins are loaded if requested
- for ((_, info) in availableClocks) {
- info.manager?.loadPlugin()
+ // TODO(b/267372164): Use better threading approach when converting to flows
+ synchronized(availableClocks) {
+ isQueued.set(false)
+ if (keepAllLoaded) {
+ logger.tryLog(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "verifyLoadedProviders: keepAllLoaded=true" }
+ )
+ // Enforce that all plugins are loaded if requested
+ for ((_, info) in availableClocks) {
+ info.manager?.loadPlugin()
+ }
+ return@launch
+ }
+
+ val currentClock = availableClocks[currentClockId]
+ if (currentClock == null) {
+ logger.tryLog(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "verifyLoadedProviders: currentClock=null" }
+ )
+ // Current Clock missing, load no plugins and use default
+ for ((_, info) in availableClocks) {
+ info.manager?.unloadPlugin()
+ }
+ return@launch
}
- isVerifying.set(false)
- return@launch
- }
- val currentClock = availableClocks[currentClockId]
- if (currentClock == null) {
logger.tryLog(
TAG,
LogLevel.INFO,
{},
- { "verifyLoadedProviders: currentClock=null" }
+ { "verifyLoadedProviders: load currentClock" }
)
- // Current Clock missing, load no plugins and use default
- for ((_, info) in availableClocks) {
- info.manager?.unloadPlugin()
- }
- isVerifying.set(false)
- return@launch
- }
-
- logger.tryLog(TAG, LogLevel.INFO, {}, { "verifyLoadedProviders: load currentClock" })
- val currentManager = currentClock.manager
- currentManager?.loadPlugin()
+ val currentManager = currentClock.manager
+ currentManager?.loadPlugin()
- for ((_, info) in availableClocks) {
- val manager = info.manager
- if (manager != null && currentManager != manager) {
- manager.unloadPlugin()
+ for ((_, info) in availableClocks) {
+ val manager = info.manager
+ if (manager != null && currentManager != manager) {
+ manager.unloadPlugin()
+ }
}
}
- isVerifying.set(false)
}
}
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
index 7ce1ba3be7f6..d7d75d4304d0 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -20,17 +20,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:paddingMode="stack" >
+ <!-- The groove used for indicating max volume !-->
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
- <inset
- android:insetLeft="@dimen/rounded_slider_track_inset"
- android:insetRight="@dimen/rounded_slider_track_inset" >
- <shape>
- <size android:height="@dimen/volume_dialog_track_width" />
- <corners android:radius="@dimen/volume_dialog_track_corner_radius" />
- <solid android:color="?androidprv:attr/colorAccentSecondaryVariant" />
- </shape>
- </inset>
+ <shape>
+ <size android:height="@dimen/volume_dialog_track_width" />
+ <corners android:radius="@dimen/volume_dialog_panel_width_half" />
+ <solid android:color="?androidprv:attr/materialColorOutlineVariant" />
+ </shape>
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 37b8ae0f40c4..c70f8e2b1c07 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -22,8 +22,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:paddingStart="4dp"
- android:paddingEnd="4dp"
->
+ android:paddingEnd="4dp">
<LinearLayout
android:id="@+id/half_shelf"
@@ -82,11 +81,21 @@
android:theme="@style/MainSwitch.Settingslib"/>
</com.android.systemui.statusbar.notification.row.AppControlView>
- <!-- ChannelRows get added dynamically -->
-
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_blocker_channel_list_height"
+ android:clipToPadding="false">
+ <LinearLayout
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <!-- ChannelRows get added dynamically -->
+ </LinearLayout>
+ </ScrollView>
</com.android.systemui.statusbar.notification.row.ChannelEditorListView>
- <RelativeLayout
+ <LinearLayout
android:id="@+id/bottom_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -98,25 +107,23 @@
android:text="@string/see_more_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
style="@style/Widget.Dialog.Button"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
<TextView
android:id="@+id/done_button"
android:text="@string/inline_ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:gravity="end|center_vertical"
android:maxWidth="125dp"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
- android:layout_alignParentEnd="true"
style="@style/Widget.Dialog.Button"/>
- </RelativeLayout>
+ </LinearLayout>
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 85fb3ac577bc..587caaf3ecf3 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -44,6 +44,6 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 259b9adf0209..cfb40171cfc1 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -89,6 +89,9 @@
<dimen name="global_actions_button_size">72dp</dimen>
<dimen name="global_actions_button_padding">26dp</dimen>
+ <!-- scroll view the size of 2 channel rows -->
+ <dimen name="notification_blocker_channel_list_height">128dp</dimen>
+
<dimen name="keyguard_indication_margin_bottom">8dp</dimen>
<dimen name="lock_icon_margin_bottom">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index e63229aea70b..fc6d20e11d3b 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -38,6 +38,6 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b6bca65b8174..3bdc0af7f7af 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -604,7 +604,7 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">false</bool>
<!-- Whether we use large screen shade header which takes only one row compared to QS header -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 88726af39c25..0ee5da22a31b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -313,6 +313,8 @@
<!-- The space around a notification menu item -->
<dimen name="notification_menu_icon_padding">20dp</dimen>
+ <!-- scroll view the size of 3 channel rows -->
+ <dimen name="notification_blocker_channel_list_height">192dp</dimen>
<!-- The vertical space around the buttons in the inline settings -->
<dimen name="notification_guts_button_spacing">12dp</dimen>
@@ -545,7 +547,7 @@
<!-- (volume_dialog_panel_width - rounded_slider_icon_size) / 2 -->
<dimen name="volume_slider_icon_inset">11dp</dimen>
- <dimen name="volume_dialog_track_width">4dp</dimen>
+ <dimen name="volume_dialog_track_width">40dp</dimen>
<dimen name="volume_dialog_track_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 21696fefb80f..6cdd15e637bd 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -233,6 +233,11 @@
<item type="id" name="pin_pad"/>
<!--
+ Tag used to store pending intent registration listeners in NotificationTemplateViewWrapper
+ -->
+ <item type="id" name="pending_intent_listener_tag" />
+
+ <!--
Used to tag views programmatically added to the smartspace area so they can be more easily
removed later.
-->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 905923039f8b..c0749885846f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -282,9 +282,9 @@ public class RotationButtonController {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
}
- public void setRotationLockedAtAngle(int rotationSuggestion) {
+ public void setRotationLockedAtAngle(int rotationSuggestion, String caller) {
RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isRotationLocked(),
- /* rotation= */ rotationSuggestion);
+ /* rotation= */ rotationSuggestion, caller);
}
public boolean isRotationLocked() {
@@ -468,7 +468,8 @@ public class RotationButtonController {
if (rotationLocked || mRotationButton.isVisible()) {
// Do not allow a change in rotation to set user rotation when docked.
if (shouldOverrideUserLockPrefs(rotation) && rotationLocked && !mDocked) {
- setRotationLockedAtAngle(rotation);
+ setRotationLockedAtAngle(rotation, /* caller= */
+ "RotationButtonController#onRotationWatcherChanged");
}
setRotateSuggestionButtonState(false /* visible */, true /* forced */);
}
@@ -572,7 +573,8 @@ public class RotationButtonController {
private void onRotateSuggestionClick(View v) {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
- setRotationLockedAtAngle(mLastRotationSuggestion);
+ setRotationLockedAtAngle(mLastRotationSuggestion,
+ /* caller= */ "RotationButtonController#onRotateSuggestionClick");
Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion);
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
index 96a974d5dd80..7b2e1afda700 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
@@ -19,6 +19,12 @@ package com.android.systemui.util
import android.os.Trace
import android.os.TraceNameSupplier
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
/**
* Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
@@ -85,5 +91,18 @@ class TraceUtils {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, cookie)
}
}
+
+ /**
+ * Convenience method to avoid one indentation level when we want to add a trace when
+ * launching a coroutine
+ */
+ fun <T> CoroutineScope.tracedAsync(
+ method: String,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+ block: suspend () -> T
+ ): Deferred<T> {
+ return async(context, start) { traceAsync(method) { block() } }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index ba8e42752586..f6a0563ebf94 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -184,6 +184,10 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
}
+ public boolean getSplitShadeCentered() {
+ return mSplitShadeCentered;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 29414ea7f4c0..5646abefcef1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -234,6 +234,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
}
+ public KeyguardClockSwitch getView() {
+ return mView;
+ }
+
private void hideSliceViewAndNotificationIconContainer() {
View ksv = mView.findViewById(R.id.keyguard_slice_view);
ksv.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 625c1de0134d..b2287d876a48 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -163,7 +163,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
mCurrentUser = KeyguardUpdateMonitor.getCurrentUser();
showPrimarySecurityScreen(false);
- reinflateViewFlipper((l) -> {});
+ if (mCurrentSecurityMode != SecurityMode.SimPin
+ && mCurrentSecurityMode != SecurityMode.SimPuk) {
+ reinflateViewFlipper((l) -> {});
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index d8486029a903..f9cc03eea288 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import static java.util.Collections.emptySet;
+import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
@@ -78,6 +79,14 @@ public class KeyguardStatusView extends GridLayout {
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mMediaHostContainer = findViewById(R.id.status_view_media_container);
+ if (mMediaHostContainer != null) {
+ LayoutTransition mediaLayoutTransition = new LayoutTransition();
+ ((ViewGroup) mMediaHostContainer).setLayoutTransition(mediaLayoutTransition);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ }
updateDark();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 8d0d299e56c2..931ba6d97c58 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -23,6 +23,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CL
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
+import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.res.Configuration;
@@ -101,6 +102,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final Rect mClipBounds = new Rect();
private final KeyguardInteractor mKeyguardInteractor;
+ private Boolean mSplitShadeEnabled = false;
private Boolean mStatusViewCentered = true;
private DumpManager mDumpManager;
@@ -150,6 +152,48 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
@Override
public void onInit() {
mKeyguardClockSwitchController.init();
+ final View mediaHostContainer = mView.findViewById(R.id.status_view_media_container);
+ if (mediaHostContainer != null) {
+ mKeyguardClockSwitchController.getView().addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (!mSplitShadeEnabled
+ || mKeyguardClockSwitchController.getView().getSplitShadeCentered()
+ // Note: isKeyguardVisible() returns false after Launcher -> AOD.
+ || !mKeyguardUpdateMonitor.isKeyguardVisible()) {
+ return;
+ }
+
+ int oldHeight = oldBottom - oldTop;
+ if (v.getHeight() == oldHeight) return;
+
+ if (mediaHostContainer.getVisibility() != View.VISIBLE
+ // If the media is appearing, also don't do the transition.
+ || mediaHostContainer.getHeight() == 0) {
+ return;
+ }
+
+ final LayoutTransition mediaLayoutTransition =
+ ((ViewGroup) mediaHostContainer).getLayoutTransition();
+ if (mediaLayoutTransition == null) return;
+
+ mediaLayoutTransition.enableTransitionType(LayoutTransition.CHANGING);
+ });
+
+ mediaHostContainer.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ final LayoutTransition mediaLayoutTransition =
+ ((ViewGroup) mediaHostContainer).getLayoutTransition();
+ if (mediaLayoutTransition == null) return;
+ if (!mediaLayoutTransition.isTransitionTypeEnabled(
+ LayoutTransition.CHANGING)) {
+ return;
+ }
+ // Note: when this is called, the LayoutTransition is already been set up.
+ // Disables the LayoutTransition until it's explicitly enabled again.
+ mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGING);
+ }
+ );
+ }
mDumpManager.registerDumpable(getInstanceName(), this);
if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
@@ -385,6 +429,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
*/
public void setSplitShadeEnabled(boolean enabled) {
mKeyguardClockSwitchController.setSplitShadeEnabled(enabled);
+ mSplitShadeEnabled = enabled;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 165c4bb018d3..a81069a1f7db 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -424,11 +424,11 @@ public class LockIconViewController implements Dumpable {
private void updateConfiguration() {
WindowManager windowManager = mContext.getSystemService(WindowManager.class);
Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
mWidthPixels = bounds.right;
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
// Assumed to be initially neglected as there are no left or right insets in portrait
// However, on landscape, these insets need to included when calculating the midpoint
+ WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
}
mHeightPixels = bounds.bottom;
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 954129ef78c8..22bd20767e14 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -18,8 +18,8 @@ package com.android.systemui;
import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X;
import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
-
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+import static com.android.systemui.flags.Flags.SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import android.animation.Animator;
@@ -482,7 +482,14 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
boolean wasRemoved = false;
if (animView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
- wasRemoved = row.isRemoved();
+ if (mFeatureFlags.isEnabled(SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX)) {
+ // If the view is already removed from its parent and added as Transient,
+ // we need to clean the transient view upon animation end
+ wasRemoved = row.getTransientContainer() != null
+ || row.getParent() == null || row.isRemoved();
+ } else {
+ wasRemoved = row.isRemoved();
+ }
}
if (!mCancelled || wasRemoved) {
mCallback.onChildDismissed(animView);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 46d3c8a0c3e3..79d9c1ba70bc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -1096,8 +1096,19 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// cancel the fingerprint scan.
mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp,
AOD_SEND_FINGER_UP_DELAY_MILLIS);
- // using a hard-coded value for major and minor until it is available from the sensor
- onFingerDown(requestId, screenX, screenY, minor, major);
+ // using a hard-coded value for orientation, time and gestureStart until they are
+ // available from the sensor.
+ onFingerDown(
+ requestId,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ screenX,
+ screenY,
+ minor,
+ major,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ true /* isAod */);
};
if (mScreenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index bae0ac74548d..f3a463ba44a4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -235,17 +235,16 @@ constructor(
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
- /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */
- fun hide(
- loggingReason: String,
- ) {
+ /** Notifies the interactor that the input method editor has been hidden. */
+ fun onImeHidden() {
+ // If the bouncer is showing, hide it and return to the lockscreen scene.
if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
return
}
sceneInteractor.changeScene(
scene = SceneModel(SceneKey.Lockscreen),
- loggingReason = loggingReason,
+ loggingReason = "IME hidden",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 0b0a8f5e00f1..66c6162533bf 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -78,10 +78,7 @@ sealed class AuthMethodBouncerViewModel(
*/
fun onImeVisibilityChanged(isVisible: Boolean) {
if (isImeVisible && !isVisible) {
- // The IME has gone from visible to invisible, dismiss the bouncer.
- interactor.hide(
- loggingReason = "IME hidden",
- )
+ interactor.onImeHidden()
}
isImeVisible = isVisible
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4b38267d98e8..871257e6be79 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -298,6 +298,16 @@ object Flags {
@JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
unreleasedFlag("migrate_keyguard_status_bar_view")
+ /** Migrate clocks from keyguard status view to keyguard root view*/
+ // TODO(b/301502635): Tracking Bug.
+ @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
+ unreleasedFlag("migrate_clocks_to_blueprint")
+
+ /** Migrate KeyguardRootView to use composables. */
+ // TODO(b/301969856): Tracking Bug.
+ @JvmField val KEYGUARD_ROOT_VIEW_USE_COMPOSE =
+ unreleasedFlag("keyguard_root_view_use_compose")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -763,6 +773,13 @@ object Flags {
// TODO(b/289573946): Tracking Bug
@JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
+ // TODO(b/302087895): Tracking Bug
+ @JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+
+ // TODO(b/302144438): Tracking Bug
+ @JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
+ unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
+
// 2900 - CentralSurfaces-related flags
// TODO(b/285174336): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index fb02c7d68a9f..1761ca86f588 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,7 +26,6 @@ import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -40,7 +39,9 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -69,6 +70,7 @@ constructor(
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
+ private val shadeInteractor: ShadeInteractor,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -135,6 +137,7 @@ constructor(
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
+ shadeInteractor
)
}
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 06cf7235ad2c..e8740a4b24c5 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
@@ -26,7 +26,6 @@ import android.os.UserHandle
import android.util.Log
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
@@ -40,6 +39,8 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
import javax.inject.Inject
@@ -133,6 +134,7 @@ constructor(
devicePostureRepository: DevicePostureRepository,
facePropertyRepository: FacePropertyRepository,
fingerprintPropertyRepository: FingerprintPropertyRepository,
+ mobileConnectionsRepository: MobileConnectionsRepository,
dumpManager: DumpManager,
) : BiometricSettingsRepository, Dumpable {
@@ -346,14 +348,15 @@ constructor(
.and(isFingerprintBiometricAllowed)
.stateIn(scope, SharingStarted.Eagerly, false)
- override val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
- get() = isFaceAuthenticationEnabled.and(isFaceEnrolled)
+ override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> =
+ isFaceAuthenticationEnabled
+ .and(isFaceEnrolled)
+ .and(mobileConnectionsRepository.isAnySimSecure.isFalse())
- override val isFaceAuthCurrentlyAllowed: Flow<Boolean>
- get() =
- isFaceAuthEnrolledAndEnabled
- .and(isFaceBiometricsAllowed)
- .and(isFaceAuthSupportedInCurrentPosture)
+ override val isFaceAuthCurrentlyAllowed: Flow<Boolean> =
+ isFaceAuthEnrolledAndEnabled
+ .and(isFaceBiometricsAllowed)
+ .and(isFaceAuthSupportedInCurrentPosture)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -426,3 +429,5 @@ private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
this.combine(anotherFlow) { a, b -> a && b }
+
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> = this.map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index dd7eee924007..abc30efec716 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -25,6 +25,8 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.widget.ImageView
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -383,6 +385,27 @@ object KeyguardBottomAreaViewBinder {
falsingManager,
)
view.setOnTouchListener(onTouchListener)
+ view.setOnClickListener {
+ messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
+ val amplitude =
+ view.context.resources
+ .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
+ .toFloat()
+ val shakeAnimator =
+ ObjectAnimator.ofFloat(
+ view,
+ "translationX",
+ -amplitude / 2,
+ amplitude / 2,
+ )
+ shakeAnimator.duration =
+ KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
+ shakeAnimator.interpolator =
+ CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.start()
+
+ vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ }
view.onLongClickListener =
OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index 125e2dac7bb7..f2d39dabb1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -99,41 +99,7 @@ class KeyguardQuickAffordanceOnTouchListener(
// When not using a stylus, lifting the finger/pointer will actually cancel
// the long-press gesture. Calling cancel after the quick affordance was
// already long-press activated is a no-op, so it's safe to call from here.
- cancel(
- onAnimationEnd =
- if (event.eventTime - event.downTime < longPressDurationMs) {
- Runnable {
- messageDisplayer.invoke(
- R.string.keyguard_affordance_press_too_short
- )
- val amplitude =
- view.context.resources
- .getDimensionPixelSize(
- R.dimen.keyguard_affordance_shake_amplitude
- )
- .toFloat()
- val shakeAnimator =
- ObjectAnimator.ofFloat(
- view,
- "translationX",
- -amplitude / 2,
- amplitude / 2,
- )
- shakeAnimator.duration =
- KeyguardBottomAreaVibrations.ShakeAnimationDuration
- .inWholeMilliseconds
- shakeAnimator.interpolator =
- CycleInterpolator(
- KeyguardBottomAreaVibrations.ShakeAnimationCycles
- )
- shakeAnimator.start()
-
- vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
- }
- } else {
- null
- }
- )
+ cancel()
}
false
}
@@ -168,10 +134,10 @@ class KeyguardQuickAffordanceOnTouchListener(
view.setOnClickListener(null)
}
- fun cancel(onAnimationEnd: Runnable? = null) {
+ fun cancel() {
longPressAnimator?.cancel()
longPressAnimator = null
- view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
+ view.animate().scaleX(1f).scaleY(1f)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index eeb4ac34bf37..aa76702dc3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -23,6 +23,8 @@ import android.util.Size
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@@ -216,6 +218,27 @@ object KeyguardQuickAffordanceViewBinder {
falsingManager,
)
view.setOnTouchListener(onTouchListener)
+ view.setOnClickListener {
+ messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
+ val amplitude =
+ view.context.resources
+ .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
+ .toFloat()
+ val shakeAnimator =
+ ObjectAnimator.ofFloat(
+ view,
+ "translationX",
+ -amplitude / 2,
+ amplitude / 2,
+ )
+ shakeAnimator.duration =
+ KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
+ shakeAnimator.interpolator =
+ CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.start()
+
+ vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+ }
view.onLongClickListener =
OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index f564d0073e42..053727ace76d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -32,6 +32,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.ViewPriority
@@ -55,6 +56,7 @@ object KeyguardRootViewBinder {
occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
chipbarCoordinator: ChipbarCoordinator,
keyguardStateController: KeyguardStateController,
+ shadeInteractor: ShadeInteractor,
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
@@ -88,6 +90,17 @@ object KeyguardRootViewBinder {
}
}
}
+
+ launch {
+ shadeInteractor.isAnyFullyExpanded.collect { isFullyAnyExpanded ->
+ view.visibility =
+ if (isFullyAnyExpanded) {
+ View.INVISIBLE
+ } else {
+ View.VISIBLE
+ }
+ }
+ }
}
repeatOnLifecycle(Lifecycle.State.STARTED) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 2ad74fbc6674..864e345e9c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -62,6 +62,7 @@ import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessage
import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
@@ -109,6 +110,7 @@ constructor(
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardStateController: KeyguardStateController,
+ private val shadeInteractor: ShadeInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -317,6 +319,7 @@ constructor(
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
+ shadeInteractor,
)
)
rootView.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 9409036586e8..f4bc7137b50c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -29,11 +29,11 @@ import androidx.constraintlayout.widget.ConstraintSet
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
-import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
@@ -73,11 +73,11 @@ constructor(
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
val bounds = windowManager.currentWindowMetrics.bounds
- val insets = windowManager.currentWindowMetrics.windowInsets
var widthPixels = bounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
// Assumed to be initially neglected as there are no left or right insets in portrait.
// However, on landscape, these insets need to included when calculating the midpoint.
+ val insets = windowManager.currentWindowMetrics.windowInsets
widthPixels -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat()
}
val heightPixels = bounds.bottom.toFloat()
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 6c2ce7fa5156..1943b340b9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -29,6 +29,7 @@ import com.android.systemui.log.LogcatEchoTrackerDebug;
import com.android.systemui.log.LogcatEchoTrackerProd;
import com.android.systemui.log.table.TableLogBuffer;
import com.android.systemui.log.table.TableLogBufferFactory;
+import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.util.Compile;
import com.android.systemui.util.wakelock.WakeLockLog;
@@ -229,12 +230,12 @@ public class LogModule {
}
/**
- * Provides a logging buffer for logs related to {@link com.android.systemui.qs.QSFragment}'s
+ * Provides a logging buffer for logs related to {@link QSFragmentLegacy}'s
* disable flag adjustments.
*/
@Provides
@SysUISingleton
- @QSFragmentDisableLog
+ @QSDisableLog
public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
return factory.create("QSFragmentDisableFlagsLog", 10 /* maxSize */,
false /* systrace */);
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
index 557a254e5c09..b3bceca57f14 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
@@ -19,6 +19,7 @@ package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
+import com.android.systemui.qs.QSFragmentLegacy;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -27,10 +28,10 @@ import javax.inject.Qualifier;
/**
* A {@link LogBuffer} for disable flag adjustments made in
- * {@link com.android.systemui.qs.QSFragment}.
+ * {@link QSFragmentLegacy}.
*/
@Qualifier
@Documented
@Retention(RUNTIME)
-public @interface QSFragmentDisableLog {
+public @interface QSDisableLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
index fbf9294a0a37..11d0be5fc8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
@@ -14,20 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.media
+package com.android.systemui.mediaprojection
import android.os.IBinder
import android.os.Parcel
import android.os.Parcelable
/**
- * Class that represents an area that should be captured.
- * Currently it has only a launch cookie that represents a task but
- * we potentially could add more identifiers e.g. for a pair of tasks.
+ * Class that represents an area that should be captured. Currently it has only a launch cookie that
+ * represents a task but we potentially could add more identifiers e.g. for a pair of tasks.
*/
-data class MediaProjectionCaptureTarget(
- val launchCookie: IBinder?
-): Parcelable {
+data class MediaProjectionCaptureTarget(val launchCookie: IBinder?) : Parcelable {
constructor(parcel: Parcel) : this(parcel.readStrongBinder())
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
index 9e616e2355e2..f1cade7512e2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media
+package com.android.systemui.mediaprojection
import android.content.Context
import android.media.projection.IMediaProjection
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 88bc064c60f7..b5d3e913cadb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.media
+package com.android.systemui.mediaprojection.appselector
import android.app.ActivityOptions
import android.content.Intent
@@ -46,13 +46,11 @@ import com.android.internal.app.chooser.TargetInfo
import com.android.internal.widget.RecyclerView
import com.android.internal.widget.RecyclerViewAccessibilityDelegate
import com.android.internal.widget.ResolverDrawerLayout
-import com.android.systemui.res.R
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorView
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.AsyncActivityLauncher
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 33d9cc36c9b0..72aea040ba05 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -23,8 +23,6 @@ import android.os.UserHandle
import androidx.lifecycle.DefaultLifecycleObserver
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionPermissionActivity
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
@@ -37,6 +35,7 @@ import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRece
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
+import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
index 64006fe4c265..8b437c322549 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenrecord
+package com.android.systemui.mediaprojection.permission
import android.content.Context
import android.os.Bundle
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 4de6278400b3..2b56d0cf9f83 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media;
+package com.android.systemui.mediaprojection.permission;
import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
@@ -22,8 +22,8 @@ import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
-import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
+import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
+import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
import android.annotation.Nullable;
import android.app.Activity;
@@ -51,13 +51,13 @@ import android.text.style.StyleSpan;
import android.util.Log;
import android.view.Window;
-import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
-import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
-import com.android.systemui.screenrecord.ScreenShareOption;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
index 47e28d86e01d..2f10ad3e6486 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenrecord
+package com.android.systemui.mediaprojection.permission
import android.content.Context
import android.media.projection.MediaProjectionConfig
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
index ebf0dd28fbf4..37e8d9f26ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenrecord
+package com.android.systemui.mediaprojection.permission
import androidx.annotation.IntDef
import androidx.annotation.StringRes
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index cb52a5fb53fa..ad1c77d05534 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -795,7 +795,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
// Reset user rotation pref to match that of the WindowManager if starting in locked
// mode. This will automatically happen when switching from auto-rotate to locked mode.
if (display != null && rotationButtonController.isRotationLocked()) {
- rotationButtonController.setRotationLockedAtAngle(display.getRotation());
+ rotationButtonController.setRotationLockedAtAngle(
+ display.getRotation(), /* caller= */ "NavigationBar#onViewAttached");
}
} else {
mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 826f75f2f63b..19012e29b184 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -78,6 +78,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private int mMinRows = 1;
private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
+ /**
+ * it's fine to read this value when class is initialized because SysUI is always restarted
+ * when running tests in test harness, see SysUiTestIsolationRule. This check is done quite
+ * often - with every shade open action - so we don't want to potentially make it less
+ * performant only for test use case
+ */
+ private boolean mRunningInTestHarness = ActivityManager.isRunningInTestHarness();
+
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context, SCROLL_CUBIC);
@@ -590,11 +598,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private boolean shouldNotRunAnimation(Set<String> tilesToReveal) {
boolean noAnimationNeeded = tilesToReveal.isEmpty() || mPages.size() < 2;
boolean scrollingInProgress = getScrollX() != 0 || !beginFakeDrag();
- // isRunningInTestHarness() to disable animation in functional testing as it caused
+ // checking mRunningInTestHarness to disable animation in functional testing as it caused
// flakiness and is not needed there. Alternative solutions were more complex and would
// still be either potentially flaky or modify internal data.
// For more info see b/253493927 and b/293234595
- return noAnimationNeeded || scrollingInProgress || ActivityManager.isRunningInTestHarness();
+ return noAnimationNeeded || scrollingInProgress || mRunningInTestHarness;
}
private int sanitizePageAction(int action) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 463c79c6696a..eba1c25d0e74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -28,7 +28,7 @@ import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -86,8 +86,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private final QuickQSPanel mQuickQsPanel;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
- private final QuickStatusBarHeader mQuickStatusBarHeader;
- private final QS mQs;
+ private final View mQsRootView;
@Nullable
private PagedTileLayout mPagedLayout;
@@ -115,8 +114,6 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
// Brightness slider opacity driver. Uses linear interpolator.
@Nullable
private TouchAnimator mBrightnessOpacityAnimator;
- // Animator for Footer actions in QQS
- private TouchAnimator mQQSFooterActionsAnimator;
// Height animator for QQS tiles (height changing from QQS size to QS size)
@Nullable
private HeightExpansionAnimator mQQSTileHeightAnimator;
@@ -144,22 +141,21 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private int[] mTmpLoc2 = new int[2];
@Inject
- public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+ public QSAnimator(@RootView View rootView, QuickQSPanel quickPanel,
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSHost qsTileHost,
@Main Executor executor, TunerService tunerService,
QSExpansionPathInterpolator qsExpansionPathInterpolator) {
- mQs = qs;
+ mQsRootView = rootView;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
- mQuickStatusBarHeader = quickStatusBarHeader;
mHost = qsTileHost;
mExecutor = executor;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
- qs.getView().addOnLayoutChangeListener(this);
+ mQsRootView.addOnLayoutChangeListener(this);
if (mQsPanelController.isAttachedToWindow()) {
onViewAttachedToWindow(null);
}
@@ -314,8 +310,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
break;
}
- final View tileIcon = tileView.getIcon().getIconView();
- View view = mQs.getView();
+ View view = mQsRootView;
// This case: less tiles to animate in small displays.
if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()) {
@@ -480,7 +475,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
.setStartDelay(QS_TILE_LABEL_FADE_OUT_START)
.setEndDelay(QS_TILE_LABEL_FADE_OUT_END);
SideLabelTileLayout qqsLayout = (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
- View view = mQs.getView();
+ View view = mQsRootView;
List<String> specs = mPagedLayout.getSpecsForPage(page);
if (specs.isEmpty()) {
// specs should not be empty in a valid secondary page, as we scrolled to it.
@@ -577,7 +572,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
// For (1), compute the distance via the vertical distance between QQS and QS tile
// layout top.
- View quickSettingsRootView = mQs.getView();
+ View quickSettingsRootView = mQsRootView;
View qsTileLayout = (View) mQsPanelController.getTileLayout();
View qqsTileLayout = (View) mQuickQSPanelController.getTileLayout();
getRelativePosition(mTmpLoc1, qsTileLayout, quickSettingsRootView);
@@ -607,7 +602,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private int getRelativeTranslationY(View view1, View view2) {
int[] qsPosition = new int[2];
int[] qqsPosition = new int[2];
- View commonView = mQs.getView();
+ View commonView = mQsRootView;
getRelativePositionInt(qsPosition, view1, commonView);
getRelativePositionInt(qqsPosition, view2, commonView);
return qsPosition[1] - qqsPosition[1];
@@ -690,9 +685,6 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
if (mBrightnessTranslationAnimator != null) {
mBrightnessTranslationAnimator.setPosition(position);
}
- if (mQQSFooterActionsAnimator != null) {
- mQQSFooterActionsAnimator.setPosition(position);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
index 6563e425190d..6f6f467170c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
@@ -1,20 +1,22 @@
package com.android.systemui.qs
-import com.android.systemui.log.dagger.QSFragmentDisableLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.QSDisableLog
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
-/** A helper class for logging disable flag changes made in [QSFragment]. */
-class QSFragmentDisableFlagsLogger @Inject constructor(
- @QSFragmentDisableLog private val buffer: LogBuffer,
+/** A helper class for logging disable flag changes made in [QSImpl]. */
+class QSDisableFlagsLogger
+@Inject
+constructor(
+ @QSDisableLog private val buffer: LogBuffer,
private val disableFlagsLogger: DisableFlagsLogger
) {
/**
- * Logs a string representing the new state received by [QSFragment] and any modifications that
- * were made to the flags locally.
+ * Logs a string representing the new state received by [QSImpl] and any modifications that were
+ * made to the flags locally.
*
* @param new see [DisableFlagsLogger.getDisableFlagsString]
* @param newAfterLocalModification see [DisableFlagsLogger.getDisableFlagsString]
@@ -43,4 +45,4 @@ class QSFragmentDisableFlagsLogger @Inject constructor(
}
}
-private const val TAG = "QSFragmentDisableFlagsLog"
+private const val TAG = "QSDisableFlagsLog"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
new file mode 100644
index 000000000000..8589ae9305fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
+import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.LifecycleFragment;
+
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+public class QSFragmentLegacy extends LifecycleFragment implements QS {
+
+ private final Provider<QSImpl> mQsImplProvider;
+
+ private final QSFragmentComponent.Factory mQsComponentFactory;
+
+ @Nullable
+ private QSImpl mQsImpl;
+
+ @Inject
+ public QSFragmentLegacy(
+ Provider<QSImpl> qsImplProvider,
+ QSFragmentComponent.Factory qsComponentFactory
+ ) {
+ mQsComponentFactory = qsComponentFactory;
+ mQsImplProvider = qsImplProvider;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ try {
+ Trace.beginSection("QSFragment#onCreateView");
+ inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+ R.style.Theme_SystemUI_QuickSettings));
+ return inflater.inflate(R.layout.qs_panel, container, false);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
+ mQsImpl = mQsImplProvider.get();
+ mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
+ }
+
+ @Override
+ public void setScrollListener(ScrollListener listener) {
+ if (mQsImpl != null) {
+ mQsImpl.setScrollListener(listener);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (mQsImpl != null) {
+ mQsImpl.onCreate(savedInstanceState);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mQsImpl != null) {
+ mQsImpl.onDestroy();
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mQsImpl != null) {
+ mQsImpl.onSaveInstanceState(outState);
+ }
+ }
+
+ @Override
+ public View getHeader() {
+ if (mQsImpl != null) {
+ return mQsImpl.getHeader();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void setHasNotifications(boolean hasNotifications) {
+ if (mQsImpl != null) {
+ mQsImpl.setHasNotifications(hasNotifications);
+ }
+ }
+
+ @Override
+ public void setPanelView(HeightListener panelView) {
+ if (mQsImpl != null) {
+ mQsImpl.setPanelView(panelView);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mQsImpl != null) {
+ mQsImpl.onConfigurationChanged(newConfig);
+ }
+ }
+
+ @Override
+ public void setFancyClipping(int leftInset, int top, int rightInset, int bottom,
+ int cornerRadius, boolean visible, boolean fullWidth) {
+ if (mQsImpl != null) {
+ mQsImpl.setFancyClipping(leftInset, top, rightInset, bottom, cornerRadius, visible,
+ fullWidth);
+ }
+ }
+
+ @Override
+ public boolean isFullyCollapsed() {
+ if (mQsImpl != null) {
+ return mQsImpl.isFullyCollapsed();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public void setCollapsedMediaVisibilityChangedListener(Consumer<Boolean> listener) {
+ if (mQsImpl != null) {
+ mQsImpl.setCollapsedMediaVisibilityChangedListener(listener);
+ }
+ }
+
+ @Override
+ public void setContainerController(QSContainerController controller) {
+ if (mQsImpl != null) {
+ mQsImpl.setContainerController(controller);
+ }
+ }
+
+ @Override
+ public boolean isCustomizing() {
+ if (mQsImpl != null) {
+ return mQsImpl.isCustomizing();
+ } else {
+ return false;
+ }
+ }
+
+ public QSPanelController getQSPanelController() {
+ if (mQsImpl != null) {
+ return mQsImpl.getQSPanelController();
+ } else {
+ return null;
+ }
+ }
+
+ public void setBrightnessMirrorController(
+ BrightnessMirrorController brightnessMirrorController) {
+ if (mQsImpl != null) {
+ mQsImpl.setBrightnessMirrorController(brightnessMirrorController);
+ }
+ }
+
+ @Override
+ public boolean isShowingDetail() {
+ if (mQsImpl != null) {
+ return mQsImpl.isShowingDetail();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void setHeaderClickable(boolean clickable) {
+ if (mQsImpl != null) {
+ mQsImpl.setHeaderClickable(clickable);
+ }
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ if (mQsImpl != null) {
+ mQsImpl.setExpanded(expanded);
+ }
+ }
+
+ @Override
+ public void setOverscrolling(boolean stackScrollerOverscrolling) {
+ if (mQsImpl != null) {
+ mQsImpl.setOverscrolling(stackScrollerOverscrolling);
+ }
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (mQsImpl != null) {
+ mQsImpl.setListening(listening);
+ }
+ }
+
+ @Override
+ public void setQsVisible(boolean visible) {
+ if (mQsImpl != null) {
+ mQsImpl.setQsVisible(visible);
+ }
+ }
+
+ @Override
+ public void setHeaderListening(boolean listening) {
+ if (mQsImpl != null) {
+ mQsImpl.setHeaderListening(listening);
+ }
+ }
+
+ @Override
+ public void notifyCustomizeChanged() {
+ if (mQsImpl != null) {
+ mQsImpl.notifyCustomizeChanged();
+ }
+ }
+
+ @Override
+ public void setInSplitShade(boolean inSplitShade) {
+ if (mQsImpl != null) {
+ mQsImpl.setInSplitShade(inSplitShade);
+ }
+ }
+
+ @Override
+ public void setTransitionToFullShadeProgress(
+ boolean isTransitioningToFullShade,
+ @FloatRange(from = 0.0, to = 1.0) float qsTransitionFraction,
+ @FloatRange(from = 0.0, to = 1.0) float qsSquishinessFraction) {
+ if (mQsImpl != null) {
+ mQsImpl.setTransitionToFullShadeProgress(isTransitioningToFullShade,
+ qsTransitionFraction, qsSquishinessFraction);
+ }
+ }
+
+ @Override
+ public void setOverScrollAmount(int overScrollAmount) {
+ if (mQsImpl != null) {
+ mQsImpl.setOverScrollAmount(overScrollAmount);
+ }
+ }
+
+ @Override
+ public int getHeightDiff() {
+ if (mQsImpl != null) {
+ return mQsImpl.getHeightDiff();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void setIsNotificationPanelFullWidth(boolean isFullWidth) {
+ if (mQsImpl != null) {
+ mQsImpl.setIsNotificationPanelFullWidth(isFullWidth);
+ }
+ }
+
+ @Override
+ public void setQsExpansion(float expansion, float panelExpansionFraction,
+ float proposedTranslation, float squishinessFraction) {
+ if (mQsImpl != null) {
+ mQsImpl.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ squishinessFraction);
+ }
+ }
+
+ @Override
+ public void animateHeaderSlidingOut() {
+ if (mQsImpl != null) {
+ mQsImpl.animateHeaderSlidingOut();
+ }
+ }
+
+ @Override
+ public void setCollapseExpandAction(Runnable action) {
+ if (mQsImpl != null) {
+ mQsImpl.setCollapseExpandAction(action);
+ }
+ }
+
+ @Override
+ public void closeDetail() {
+ if (mQsImpl != null) {
+ mQsImpl.closeDetail();
+ }
+ }
+
+ @Override
+ public void closeCustomizer() {
+ if (mQsImpl != null) {
+ mQsImpl.closeDetail();
+ }
+ }
+
+ /**
+ * The height this view wants to be. This is different from {@link View#getMeasuredHeight} such
+ * that during closing the detail panel, this already returns the smaller height.
+ */
+ @Override
+ public int getDesiredHeight() {
+ if (mQsImpl != null) {
+ return mQsImpl.getDesiredHeight();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void setHeightOverride(int desiredHeight) {
+ if (mQsImpl != null) {
+ mQsImpl.setHeightOverride(desiredHeight);
+ }
+ }
+
+ @Override
+ public int getQsMinExpansionHeight() {
+ if (mQsImpl != null) {
+ return mQsImpl.getQsMinExpansionHeight();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public void hideImmediately() {
+ if (mQsImpl != null) {
+ mQsImpl.hideImmediately();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
index 253560b93f1a..9fa6769fe5f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -31,10 +31,13 @@ class QSFragmentStartable
@Inject
constructor(
private val fragmentService: FragmentService,
- private val qsFragmentProvider: Provider<QSFragment>
+ private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>
) : CoreStartable {
override fun start() {
- fragmentService.addFragmentInstantiationProvider(QSFragment::class.java, qsFragmentProvider)
+ fragmentService.addFragmentInstantiationProvider(
+ QSFragmentLegacy::class.java,
+ qsFragmentLegacyProvider
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index fd81e9a5cd0a..a32a024dbb44 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -1,15 +1,17 @@
/*
- * Copyright (C) 2016 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. You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.systemui.qs;
@@ -20,21 +22,18 @@ import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Trace;
import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
import androidx.annotation.FloatRange;
@@ -47,7 +46,6 @@ import androidx.lifecycle.LifecycleRegistry;
import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
@@ -58,19 +56,20 @@ import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.util.LifecycleFragment;
import com.android.systemui.util.Utils;
import java.io.PrintWriter;
@@ -80,8 +79,8 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
-public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks,
- StatusBarStateController.StateListener, Dumpable {
+public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateController.StateListener,
+ Dumpable {
private static final String TAG = "QS";
private static final boolean DEBUG = false;
private static final String EXTRA_EXPANDED = "expanded";
@@ -113,8 +112,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final MediaHost mQsMediaHost;
private final MediaHost mQqsMediaHost;
- private final QSFragmentComponent.Factory mQsComponentFactory;
- private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
+ private final QSDisableFlagsLogger mQsDisableFlagsLogger;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
private final QSLogger mLogger;
@@ -167,14 +165,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private boolean mIsSmallScreen;
+ private CommandQueue mCommandQueue;
+
+ private View mRootView;
+
@Inject
- public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+ public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
SysuiStatusBarStateController statusBarStateController, CommandQueue commandQueue,
@Named(QS_PANEL) MediaHost qsMediaHost,
@Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
KeyguardBypassController keyguardBypassController,
- QSFragmentComponent.Factory qsComponentFactory,
- QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
+ QSDisableFlagsLogger qsDisableFlagsLogger,
DumpManager dumpManager, QSLogger qsLogger,
FooterActionsController footerActionsController,
FooterActionsViewModel.Factory footerActionsViewModelFactory,
@@ -184,12 +185,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mQsMediaHost = qsMediaHost;
mQqsMediaHost = qqsMediaHost;
- mQsComponentFactory = qsComponentFactory;
- mQsFragmentDisableFlagsLogger = qsFragmentDisableFlagsLogger;
+ mQsDisableFlagsLogger = qsDisableFlagsLogger;
mLogger = qsLogger;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
- commandQueue.observe(getLifecycle(), this);
+ mCommandQueue = commandQueue;
mBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
mDumpManager = dumpManager;
@@ -199,34 +199,23 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
}
- @Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- Bundle savedInstanceState) {
- try {
- Trace.beginSection("QSFragment#onCreateView");
- inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
- R.style.Theme_SystemUI_QuickSettings));
- return inflater.inflate(R.layout.qs_panel, container, false);
- } finally {
- Trace.endSection();
- }
- }
+ public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) {
+ mRootView = qsComponent.getRootView();
- @Override
- public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
- mQSPanelController = qsFragmentComponent.getQSPanelController();
- mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
+ mCommandQueue.addCallback(this);
+
+ mQSPanelController = qsComponent.getQSPanelController();
+ mQuickQSPanelController = qsComponent.getQuickQSPanelController();
mQSPanelController.init();
mQuickQSPanelController.init();
- mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
- this);
- bindFooterActionsView(view);
+ mQSFooterActionsViewModel = mFooterActionsViewModelFactory
+ .create(mListeningAndVisibilityLifecycleOwner);
+ bindFooterActionsView(mRootView);
mFooterActionsController.init();
- mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
+ mQSPanelScrollView = mRootView.findViewById(R.id.expanded_qs_scroll_view);
mQSPanelScrollView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
updateQsBounds();
@@ -238,26 +227,26 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
if (mScrollListener != null) {
mScrollListener.onQsPanelScrollChanged(scrollY);
}
- });
- mHeader = view.findViewById(R.id.header);
- mFooter = qsFragmentComponent.getQSFooter();
+ });
+ mHeader = mRootView.findViewById(R.id.header);
+ mFooter = qsComponent.getQSFooter();
- mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
+ mQSContainerImplController = qsComponent.getQSContainerImplController();
mQSContainerImplController.init();
mContainer = mQSContainerImplController.getView();
mDumpManager.registerDumpable(mContainer.getClass().getSimpleName(), mContainer);
- mQSAnimator = qsFragmentComponent.getQSAnimator();
- mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
+ mQSAnimator = qsComponent.getQSAnimator();
+ mQSSquishinessController = qsComponent.getQSSquishinessController();
- mQSCustomizerController = qsFragmentComponent.getQSCustomizerController();
+ mQSCustomizerController = qsComponent.getQSCustomizerController();
mQSCustomizerController.init();
mQSCustomizerController.setQs(this);
if (savedInstanceState != null) {
setQsVisible(savedInstanceState.getBoolean(EXTRA_VISIBLE));
setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
- setEditLocation(view);
+ setEditLocation(mRootView);
mQSCustomizerController.restoreInstanceState(savedInstanceState);
if (mQsExpanded) {
mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
@@ -265,7 +254,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
mStatusBarStateController.addCallback(this);
onStateChanged(mStatusBarStateController.getState());
- view.addOnLayoutChangeListener(
+ mRootView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
if (sizeChanged) {
@@ -327,15 +316,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mScrollListener = listener;
}
- @Override
public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
mDumpManager.registerDumpable(getClass().getSimpleName(), this);
}
- @Override
public void onDestroy() {
- super.onDestroy();
+ mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
if (mListening) {
setListening(false);
@@ -351,9 +337,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mListeningAndVisibilityLifecycleOwner.destroy();
}
- @Override
public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
outState.putBoolean(EXTRA_LISTENING, mListening);
outState.putBoolean(EXTRA_VISIBLE, mQsVisible);
@@ -394,9 +378,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mPanelView = panelView;
}
- @Override
public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
setEditLocation(getView());
if (newConfig.getLayoutDirection() != mLayoutDirection) {
mLayoutDirection = newConfig.getLayoutDirection();
@@ -452,9 +434,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
- mQsFragmentDisableFlagsLogger.logDisableFlagChange(
- /* new= */ new DisableState(state1, state2BeforeAdjustment),
- /* newAfterLocalModification= */ new DisableState(state1, state2)
+ mQsDisableFlagsLogger.logDisableFlagChange(
+ /* new= */ new DisableFlagsLogger.DisableState(state1, state2BeforeAdjustment),
+ /* newAfterLocalModification= */ new DisableFlagsLogger.DisableState(state1, state2)
);
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
@@ -919,32 +901,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
getView().setY(-getQsMinExpansionHeight());
}
- private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
- = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getView().getViewTreeObserver().removeOnPreDrawListener(this);
- getView().animate()
- .translationY(0f)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setListener(mAnimateHeaderSlidingInListener)
- .start();
- return true;
- }
- };
-
- private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
- = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeaderAnimating = false;
- updateQsState();
- // Unset the listener, otherwise this may persist for another view property animation
- getView().animate().setListener(null);
- }
- };
-
@Override
public void onUpcomingStateChanged(int upcomingState) {
if (upcomingState == KEYGUARD) {
@@ -1030,6 +986,20 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
return "GONE";
}
+ @Override
+ public View getView() {
+ return mRootView;
+ }
+
+ @Override
+ public Context getContext() {
+ return mRootView.getContext();
+ }
+
+ private Resources getResources() {
+ return getContext().getResources();
+ }
+
/**
* A {@link LifecycleOwner} whose state is driven by the current state of this fragment:
*
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 9359958397d1..6bbdc54d260d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -19,7 +19,7 @@ package com.android.systemui.qs;
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
import android.view.MotionEvent;
import android.view.View;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ef8167420f13..60c92c000760 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,9 +43,6 @@ import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.animation.DisappearParameters;
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
@@ -54,7 +50,8 @@ import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-import javax.inject.Named;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
/**
* Controller for QSPanel views.
@@ -135,7 +132,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
T view,
QSHost host,
QSCustomizerController qsCustomizerController,
- @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
+ boolean usingMediaPlayer,
MediaHost mediaHost,
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 9a9626d7c7a0..10f95e0b7201 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -48,6 +48,7 @@ import com.android.systemui.qs.nano.QsTileState;
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
+import com.android.systemui.qs.tiles.di.NewQSTileFactory;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -56,6 +57,8 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
@@ -121,6 +124,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
@Inject
public QSTileHost(Context context,
+ Lazy<NewQSTileFactory> newQsTileFactoryProvider,
QSFactory defaultFactory,
@Main Executor mainExecutor,
PluginManager pluginManager,
@@ -147,6 +151,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
mShadeController = shadeController;
+ if (featureFlags.getPipelineTilesEnabled()) {
+ mQsFactories.add(newQsTileFactoryProvider.get());
+ }
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
mUserTracker = userTracker;
@@ -326,7 +333,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
try {
tile = createTile(tileSpec);
if (tile != null) {
- tile.setTileSpec(tileSpec);
if (tile.isAvailable()) {
newTiles.put(tileSpec, tile);
mQSLogger.logTileAdded(tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 099d19d8619f..f278dce047e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,14 +17,13 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.media.controls.ui.MediaHost;
@@ -32,6 +31,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.leak.RotationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7888f4c7388d..a103566400a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -33,11 +33,11 @@ import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSUtils;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.LightBarController;
/**
@@ -135,8 +135,10 @@ public class QSCustomizer extends LinearLayout {
setVisibility(View.VISIBLE);
long duration = mClipper.animateCircularClip(
mX, mY, true, new ExpandAnimatorListener(tileAdapter));
- mQsContainerController.setCustomizerAnimating(true);
- mQsContainerController.setCustomizerShowing(true, duration);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(true);
+ mQsContainerController.setCustomizerShowing(true, duration);
+ }
}
}
@@ -150,8 +152,10 @@ public class QSCustomizer extends LinearLayout {
mClipper.showBackground();
isShown = true;
setCustomizing(true);
- mQsContainerController.setCustomizerAnimating(false);
- mQsContainerController.setCustomizerShowing(true);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerShowing(true);
+ }
}
}
@@ -169,8 +173,10 @@ public class QSCustomizer extends LinearLayout {
} else {
setVisibility(View.GONE);
}
- mQsContainerController.setCustomizerAnimating(animate);
- mQsContainerController.setCustomizerShowing(false, duration);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(animate);
+ mQsContainerController.setCustomizerShowing(false, duration);
+ }
}
}
@@ -180,7 +186,9 @@ public class QSCustomizer extends LinearLayout {
void setCustomizing(boolean customizing) {
mCustomizing = customizing;
- mQs.notifyCustomizeChanged();
+ if (mQs != null) {
+ mQs.notifyCustomizeChanged();
+ }
}
public boolean isCustomizing() {
@@ -208,15 +216,21 @@ public class QSCustomizer extends LinearLayout {
setCustomizing(true);
}
mOpening = false;
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
mRecyclerView.setAdapter(mTileAdapter);
}
@Override
public void onAnimationCancel(Animator animation) {
mOpening = false;
- mQs.notifyCustomizeChanged();
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQs != null) {
+ mQs.notifyCustomizeChanged();
+ }
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
}
}
@@ -226,7 +240,9 @@ public class QSCustomizer extends LinearLayout {
if (!isShown) {
setVisibility(View.GONE);
}
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
}
@Override
@@ -234,7 +250,9 @@ public class QSCustomizer extends LinearLayout {
if (!isShown) {
setVisibility(View.GONE);
}
- mQsContainerController.setCustomizerAnimating(false);
+ if (mQsContainerController != null) {
+ mQsContainerController.setCustomizerAnimating(false);
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index ce504b2c10a6..024e760e6ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -34,14 +34,14 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
-import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -199,7 +199,7 @@ public class QSCustomizerController extends ViewController<QSCustomizer> {
}
/** */
- public void setQs(@Nullable QSFragment qsFragment) {
+ public void setQs(@Nullable QS qsFragment) {
mView.setQs(qsFragment);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index a6226b36b0fd..2af7ae0614ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -130,11 +130,9 @@ public class TileQueryHelper {
if (tile == null) {
continue;
} else if (!tile.isAvailable()) {
- tile.setTileSpec(spec);
tile.destroy();
continue;
}
- tile.setTileSpec(spec);
tilesToAdd.add(tile);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt
new file mode 100644
index 000000000000..f3413b80a2ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt
@@ -0,0 +1,40 @@
+package com.android.systemui.qs.dagger
+
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.QSAnimator
+import com.android.systemui.qs.QSContainerImplController
+import com.android.systemui.qs.QSFooter
+import com.android.systemui.qs.QSPanelController
+import com.android.systemui.qs.QSSquishinessController
+import com.android.systemui.qs.QuickQSPanelController
+import com.android.systemui.qs.customize.QSCustomizerController
+
+interface QSComponent {
+ /** Construct a [QSPanelController]. */
+ fun getQSPanelController(): QSPanelController
+
+ /** Construct a [QuickQSPanelController]. */
+ fun getQuickQSPanelController(): QuickQSPanelController
+
+ /** Construct a [QSAnimator]. */
+ fun getQSAnimator(): QSAnimator
+
+ /** Construct a [QSContainerImplController]. */
+ fun getQSContainerImplController(): QSContainerImplController
+
+ /** Construct a [QSFooter] */
+ fun getQSFooter(): QSFooter
+
+ /** Construct a [QSCustomizerController]. */
+ fun getQSCustomizerController(): QSCustomizerController
+
+ /** Construct a [QSSquishinessController]. */
+ fun getQSSquishinessController(): QSSquishinessController
+
+ /** Construct a [FooterActionsController]. */
+ fun getQSFooterActionController(): FooterActionsController
+
+ @RootView fun getRootView(): View
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
new file mode 100644
index 000000000000..ba1aa629f8cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.dagger
+
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+@Subcomponent(modules = [QSFlexiglassModule::class])
+@QSScope
+interface QSFlexiglassComponent : QSComponent {
+
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(@RootView @BindsInstance rootView: View): QSFlexiglassComponent
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
new file mode 100644
index 000000000000..36fac44e0173
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.dagger
+
+import android.content.Context
+import com.android.systemui.qs.dagger.QSScopeModule.Companion.QS_USING_COLLAPSED_LANDSCAPE_MEDIA
+import com.android.systemui.qs.dagger.QSScopeModule.Companion.QS_USING_MEDIA_PLAYER
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Module(includes = [QSScopeModule::class])
+interface QSFlexiglassModule {
+
+ @Module
+ companion object {
+
+ /** */
+ @Provides
+ @Named(QS_USING_MEDIA_PLAYER)
+ @JvmStatic
+ fun providesQSUsingMediaPlayer(context: Context?): Boolean {
+ return false
+ }
+
+ /** */
+ @Provides
+ @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+ @JvmStatic
+ fun providesQSUsingCollapsedLandscapeMedia(context: Context): Boolean {
+ return false
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 594f4f86a680..327e858059f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,53 +16,21 @@
package com.android.systemui.qs.dagger;
-import com.android.systemui.qs.FooterActionsController;
-import com.android.systemui.qs.QSAnimator;
-import com.android.systemui.qs.QSContainerImplController;
-import com.android.systemui.qs.QSFooter;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.qs.QSSquishinessController;
-import com.android.systemui.qs.QuickQSPanelController;
-import com.android.systemui.qs.customize.QSCustomizerController;
+import com.android.systemui.qs.QSFragmentLegacy;
import dagger.BindsInstance;
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for {@link QSFragment}.
+ * Dagger Subcomponent for {@link QSFragmentLegacy}.
*/
@Subcomponent(modules = {QSFragmentModule.class})
@QSScope
-public interface QSFragmentComponent {
+public interface QSFragmentComponent extends QSComponent {
/** Factory for building a {@link QSFragmentComponent}. */
@Subcomponent.Factory
interface Factory {
- QSFragmentComponent create(@BindsInstance QSFragment qsFragment);
+ QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment);
}
-
- /** Construct a {@link QSPanelController}. */
- QSPanelController getQSPanelController();
-
- /** Construct a {@link QuickQSPanelController}. */
- QuickQSPanelController getQuickQSPanelController();
-
- /** Construct a {@link QSAnimator}. */
- QSAnimator getQSAnimator();
-
- /** Construct a {@link QSContainerImplController}. */
- QSContainerImplController getQSContainerImplController();
-
- /** Construct a {@link QSFooter} */
- QSFooter getQSFooter();
-
- /** Construct a {@link QSCustomizerController}. */
- QSCustomizerController getQSCustomizerController();
-
- /** Construct a {@link QSSquishinessController}. */
- QSSquishinessController getQSSquishinessController();
-
- /** Construct a {@link FooterActionsController}. */
- FooterActionsController getQSFooterActionController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index bcd98035a8b9..0c9c24df5e36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -20,21 +20,11 @@ import static com.android.systemui.util.Utils.useCollapsedMediaInLandscape;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.content.Context;
-import android.view.LayoutInflater;
import android.view.View;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSContainerImpl;
-import com.android.systemui.qs.QSFooter;
-import com.android.systemui.qs.QSFooterView;
-import com.android.systemui.qs.QSFooterViewController;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QuickQSPanel;
-import com.android.systemui.qs.QuickStatusBarHeader;
-import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.QSFragmentLegacy;
import javax.inject.Named;
@@ -45,93 +35,31 @@ import dagger.Provides;
/**
* Dagger Module for {@link QSFragmentComponent}.
*/
-@Module
-public interface QSFragmentModule {
- String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
- String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media";
+@Module(includes = {QSScopeModule.class})
+public interface QSFragmentModule {
- /**
- * Provide a context themed using the QS theme
- */
- @Provides
- @QSThemedContext
- static Context provideThemedContext(@RootView View view) {
- return view.getContext();
- }
-
- /** */
- @Provides
- @QSThemedContext
- static LayoutInflater provideThemedLayoutInflater(@QSThemedContext Context context) {
- return LayoutInflater.from(context);
- }
-
- /** */
@Provides
@RootView
- static View provideRootView(QSFragment qsFragment) {
+ static View provideRootView(QSFragmentLegacy qsFragment) {
return qsFragment.getView();
}
/** */
- @Provides
- static QSPanel provideQSPanel(@RootView View view) {
- return view.findViewById(R.id.quick_settings_panel);
- }
-
- /** */
- @Provides
- static QSContainerImpl providesQSContainerImpl(@RootView View view) {
- return view.findViewById(R.id.quick_settings_container);
- }
-
- /** */
@Binds
- QS bindQS(QSFragment qsFragment);
-
- /** */
- @Provides
- static QuickStatusBarHeader providesQuickStatusBarHeader(@RootView View view) {
- return view.findViewById(R.id.header);
- }
-
- /** */
- @Provides
- static QuickQSPanel providesQuickQSPanel(QuickStatusBarHeader quickStatusBarHeader) {
- return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
- }
+ QS bindQS(QSFragmentLegacy qsFragment);
/** */
@Provides
- static QSFooterView providesQSFooterView(@RootView View view) {
- return view.findViewById(R.id.qs_footer);
- }
-
- /** */
- @Provides
- @QSScope
- static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
- qsFooterViewController.init();
- return qsFooterViewController;
- }
-
- /** */
- @Provides
- @QSScope
- static QSCustomizer providesQSCutomizer(@RootView View view) {
- return view.findViewById(R.id.qs_customize);
- }
-
- /** */
- @Provides
- @Named(QS_USING_MEDIA_PLAYER)
+ @Named(QSScopeModule.QS_USING_MEDIA_PLAYER)
static boolean providesQSUsingMediaPlayer(Context context) {
return useQsMediaPlayer(context);
}
+
+
/** */
@Provides
- @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+ @Named(QSScopeModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
static boolean providesQSUsingCollapsedLandscapeMedia(Context context) {
return useCollapsedMediaInLandscape(context.getResources());
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 03de3a02da13..a65967a0349b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -31,6 +31,7 @@ import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -41,18 +42,18 @@ import com.android.systemui.statusbar.policy.SafetyController;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
-import java.util.Map;
-
-import javax.inject.Named;
-
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
+import java.util.Map;
+
+import javax.inject.Named;
+
/**
* Module for QS dependencies
*/
-@Module(subcomponents = {QSFragmentComponent.class},
+@Module(subcomponents = {QSFragmentComponent.class, QSFlexiglassComponent.class},
includes = {
MediaModule.class,
QSExternalModule.class,
@@ -68,6 +69,11 @@ public interface QSModule {
@Multibinds
Map<String, QSTileImpl<?>> tileMap();
+ /** A map of internal QS tile ViewModels. Ensures that this can be injected even if
+ * it is empty */
+ @Multibinds
+ Map<String, QSTileViewModel> tileViewModelMap();
+
@Provides
@SysUISingleton
static AutoTileManager provideAutoTileManager(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt
new file mode 100644
index 000000000000..e68ec4c7c0ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt
@@ -0,0 +1,92 @@
+package com.android.systemui.qs.dagger
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import com.android.systemui.qs.QSContainerImpl
+import com.android.systemui.qs.QSFooter
+import com.android.systemui.qs.QSFooterView
+import com.android.systemui.qs.QSFooterViewController
+import com.android.systemui.qs.QSPanel
+import com.android.systemui.qs.QuickQSPanel
+import com.android.systemui.qs.QuickStatusBarHeader
+import com.android.systemui.qs.customize.QSCustomizer
+import com.android.systemui.res.R
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface QSScopeModule {
+ companion object {
+ const val QS_USING_MEDIA_PLAYER = "qs_using_media_player"
+ const val QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media"
+
+ @Provides
+ @QSThemedContext
+ @JvmStatic
+ fun provideThemedContext(@RootView view: View): Context {
+ return view.context
+ }
+
+ /** */
+ @Provides
+ @QSThemedContext
+ @JvmStatic
+ fun provideThemedLayoutInflater(@QSThemedContext context: Context): LayoutInflater {
+ return LayoutInflater.from(context)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun provideQSPanel(@RootView view: View): QSPanel {
+ return view.requireViewById<QSPanel>(R.id.quick_settings_panel)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQSContainerImpl(@RootView view: View): QSContainerImpl {
+ return view.requireViewById<QSContainerImpl>(R.id.quick_settings_container)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQuickStatusBarHeader(@RootView view: View): QuickStatusBarHeader {
+ return view.requireViewById<QuickStatusBarHeader>(R.id.header)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQuickQSPanel(quickStatusBarHeader: QuickStatusBarHeader): QuickQSPanel {
+ return quickStatusBarHeader.requireViewById<QuickQSPanel>(R.id.quick_qs_panel)
+ }
+
+ /** */
+ @Provides
+ @JvmStatic
+ fun providesQSFooterView(@RootView view: View): QSFooterView {
+ return view.requireViewById<QSFooterView>(R.id.qs_footer)
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ @JvmStatic
+ fun providesQSFooter(qsFooterViewController: QSFooterViewController): QSFooter {
+ qsFooterViewController.init()
+ return qsFooterViewController
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ @JvmStatic
+ fun providesQSCutomizer(@RootView view: View): QSCustomizer {
+ return view.requireViewById<QSCustomizer>(R.id.qs_customize)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index b394a079fb00..8b2c3de18469 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -72,7 +72,7 @@ interface FooterActionsInteractor {
* Show the device monitoring dialog, expanded from [expandable] if it's not null.
*
* Important: [quickSettingsContext] *must* be the [Context] associated to the
- * [Quick Settings fragment][com.android.systemui.qs.QSFragment].
+ * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy].
*/
fun showDeviceMonitoringDialog(quickSettingsContext: Context, expandable: Expandable?)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 769cb1fe1370..64fa33ce8118 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,7 +23,6 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.android.settingslib.Utils
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -34,6 +33,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.res.R
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
import javax.inject.Named
@@ -43,7 +43,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -201,8 +200,8 @@ class FooterActionsViewModel(
* will suspend indefinitely and will need to be cancelled to stop observing.
*
* Important: [quickSettingsContext] must be the [Context] associated to the
- * [Quick Settings fragment][com.android.systemui.qs.QSFragment], and the call to this function
- * must be cancelled when that fragment is destroyed.
+ * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
+ * function must be cancelled when that fragment is destroyed.
*/
suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
footerActionsInteractor.deviceMonitoringDialogRequests.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index a4600fbbf4bd..21aaa94f9e10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -20,8 +20,12 @@ 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.qs.pipeline.data.repository.DefaultTilesQSHostRepository
+import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredBroadcastRepository
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -42,6 +46,11 @@ abstract class QSPipelineModule {
abstract fun provideTileSpecRepository(impl: TileSpecSettingsRepository): TileSpecRepository
@Binds
+ abstract fun provideDefaultTilesRepository(
+ impl: DefaultTilesQSHostRepository
+ ): DefaultTilesRepository
+
+ @Binds
abstract fun bindCurrentTilesInteractor(
impl: CurrentTilesInteractorImpl
): CurrentTilesInteractor
@@ -56,6 +65,11 @@ abstract class QSPipelineModule {
@ClassKey(QSPipelineCoreStartable::class)
abstract fun provideCoreStartable(startable: QSPipelineCoreStartable): CoreStartable
+ @Binds
+ abstract fun provideQSSettingsRestoredRepository(
+ impl: QSSettingsRestoredBroadcastRepository
+ ): QSSettingsRestoredRepository
+
companion object {
/**
* Provides a logging buffer for all logs related to the new Quick Settings pipeline to log
@@ -67,5 +81,12 @@ abstract class QSPipelineModule {
fun provideQSTileListLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create(QSPipelineLogger.TILE_LIST_TAG, maxSize = 700, systrace = false)
}
+
+ @Provides
+ @SysUISingleton
+ @QSRestoreLog
+ fun providesQSRestoreLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create(QSPipelineLogger.RESTORE_TAG, maxSize = 50, systrace = false)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt
new file mode 100644
index 000000000000..c9649292fb0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.qs.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** A [LogBuffer] for the QS pipeline to track restore of associated settings. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class QSRestoreLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt
new file mode 100644
index 000000000000..d962632407df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.qs.pipeline.data.model
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+/** Data restored from Quick Settings as part of Backup & Restore. */
+data class RestoreData(
+ val restoredTiles: List<TileSpec>,
+ val restoredAutoAddedTiles: Set<TileSpec>,
+ val userId: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
index 43a16b69d1a8..7998dfbe3f92 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
@@ -16,28 +16,19 @@
package com.android.systemui.qs.pipeline.data.repository
-import android.database.ContentObserver
-import android.provider.Settings
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import android.util.SparseArray
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.util.settings.SecureSettings
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
/** Repository to track what QS tiles have been auto-added */
interface AutoAddRepository {
/** Flow of tiles that have been auto-added */
- fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>>
+ suspend fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>>
/** Mark a tile as having been auto-added */
suspend fun markTileAdded(userId: Int, spec: TileSpec)
@@ -47,89 +38,39 @@ interface AutoAddRepository {
* multiple times.
*/
suspend fun unmarkTileAdded(userId: Int, spec: TileSpec)
+
+ suspend fun reconcileRestore(restoreData: RestoreData)
}
/**
- * Implementation that tracks the auto-added tiles stored in [Settings.Secure.QS_AUTO_ADDED_TILES].
+ * Implementation of [AutoAddRepository] that delegates to an instance of [UserAutoAddRepository]
+ * for each user.
*/
@SysUISingleton
class AutoAddSettingRepository
@Inject
-constructor(
- private val secureSettings: SecureSettings,
- @Background private val bgDispatcher: CoroutineDispatcher,
-) : AutoAddRepository {
- override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
- return conflatedCallbackFlow {
- val observer =
- object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- trySend(Unit)
- }
- }
+constructor(private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory) :
+ AutoAddRepository {
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+ private val userAutoAddRepositories = SparseArray<UserAutoAddRepository>()
- awaitClose { secureSettings.unregisterContentObserver(observer) }
- }
- .onStart { emit(Unit) }
- .map { secureSettings.getStringForUser(SETTING, userId) ?: "" }
- .distinctUntilChanged()
- .map {
- it.split(DELIMITER).map(TileSpec::create).filter { it !is TileSpec.Invalid }.toSet()
- }
- .flowOn(bgDispatcher)
+ override suspend fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
+ if (userId !in userAutoAddRepositories) {
+ val repository = userAutoAddRepositoryFactory.create(userId)
+ userAutoAddRepositories.put(userId, repository)
+ }
+ return userAutoAddRepositories.get(userId).autoAdded()
}
override suspend fun markTileAdded(userId: Int, spec: TileSpec) {
- if (spec is TileSpec.Invalid) {
- return
- }
- val added = load(userId).toMutableSet()
- if (added.add(spec)) {
- store(userId, added)
- }
+ userAutoAddRepositories.get(userId)?.markTileAdded(spec)
}
override suspend fun unmarkTileAdded(userId: Int, spec: TileSpec) {
- if (spec is TileSpec.Invalid) {
- return
- }
- val added = load(userId).toMutableSet()
- if (added.remove(spec)) {
- store(userId, added)
- }
- }
-
- private suspend fun store(userId: Int, tiles: Set<TileSpec>) {
- val toStore =
- tiles
- .filter { it !is TileSpec.Invalid }
- .joinToString(DELIMITER, transform = TileSpec::spec)
- withContext(bgDispatcher) {
- secureSettings.putStringForUser(
- SETTING,
- toStore,
- null,
- false,
- userId,
- true,
- )
- }
- }
-
- private suspend fun load(userId: Int): Set<TileSpec> {
- return withContext(bgDispatcher) {
- (secureSettings.getStringForUser(SETTING, userId) ?: "")
- .split(",")
- .map(TileSpec::create)
- .filter { it !is TileSpec.Invalid }
- .toSet()
- }
+ userAutoAddRepositories.get(userId)?.unmarkTileAdded(spec)
}
- companion object {
- private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
- private const val DELIMITER = ","
+ override suspend fun reconcileRestore(restoreData: RestoreData) {
+ userAutoAddRepositories.get(restoreData.userId)?.reconcileRestore(restoreData)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt
new file mode 100644
index 000000000000..fe0a69b03287
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+interface DefaultTilesRepository {
+ val defaultTiles: List<TileSpec>
+}
+
+@SysUISingleton
+class DefaultTilesQSHostRepository
+@Inject
+constructor(
+ @Main private val resources: Resources,
+) : DefaultTilesRepository {
+ override val defaultTiles: List<TileSpec>
+ get() =
+ QSHost.getDefaultSpecs(resources).map(TileSpec::create).filter {
+ it != TileSpec.Invalid
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
new file mode 100644
index 000000000000..6cee1161a104
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -0,0 +1,122 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+
+/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
+interface QSSettingsRestoredRepository {
+ val restoreData: Flow<RestoreData>
+}
+
+@SysUISingleton
+class QSSettingsRestoredBroadcastRepository
+@Inject
+constructor(
+ broadcastDispatcher: BroadcastDispatcher,
+ logger: QSPipelineLogger,
+ @Application private val scope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : QSSettingsRestoredRepository {
+
+ override val restoreData =
+ flow {
+ val firstIntent = mutableMapOf<Int, Intent>()
+ broadcastDispatcher
+ .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
+ intent to receiver.sendingUserId
+ }
+ .filter { it.first.isCorrectSetting() }
+ .collect { (intent, user) ->
+ if (user !in firstIntent) {
+ firstIntent[user] = intent
+ } else {
+ val firstRestored = firstIntent.remove(user)!!
+ emit(processIntents(user, firstRestored, intent))
+ }
+ }
+ }
+ .catch { Log.e(TAG, "Error parsing broadcast", it) }
+ .flowOn(backgroundDispatcher)
+ .buffer(BUFFER_CAPACITY)
+ .shareIn(scope, SharingStarted.Eagerly)
+ .onEach(logger::logSettingsRestored)
+
+ private fun processIntents(user: Int, intent1: Intent, intent2: Intent): RestoreData {
+ intent1.validateIntent()
+ intent2.validateIntent()
+ val setting1 = intent1.getStringExtra(Intent.EXTRA_SETTING_NAME)
+ val setting2 = intent2.getStringExtra(Intent.EXTRA_SETTING_NAME)
+ val (tiles, autoAdd) =
+ if (setting1 == TILES_SETTING && setting2 == AUTO_ADD_SETTING) {
+ intent1 to intent2
+ } else if (setting1 == AUTO_ADD_SETTING && setting2 == TILES_SETTING) {
+ intent2 to intent1
+ } else {
+ throw IllegalStateException("Wrong intents ($intent1, $intent2)")
+ }
+
+ return RestoreData(
+ (tiles.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
+ (autoAdd.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesSet(),
+ user,
+ )
+ }
+
+ private companion object {
+ private const val TAG = "QSSettingsRestoredBroadcastRepository"
+ // This capacity is the number of restore data that we will keep buffered in the shared
+ // flow. It is unlikely that at any given time there would be this many restores being
+ // processed by consumers, but just in case that a couple of users are restored at the
+ // same time and they need to be replayed for the consumers of the flow.
+ private const val BUFFER_CAPACITY = 10
+
+ private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
+ private const val TILES_SETTING = Settings.Secure.QS_TILES
+ private const val AUTO_ADD_SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+ private val requiredExtras =
+ listOf(
+ Intent.EXTRA_SETTING_NAME,
+ Intent.EXTRA_SETTING_PREVIOUS_VALUE,
+ Intent.EXTRA_SETTING_NEW_VALUE,
+ )
+
+ private fun Intent.isCorrectSetting(): Boolean {
+ val setting = getStringExtra(Intent.EXTRA_SETTING_NAME)
+ return setting == TILES_SETTING || setting == AUTO_ADD_SETTING
+ }
+
+ private fun Intent.validateIntent() {
+ requiredExtras.forEach { extra ->
+ if (!hasExtra(extra)) {
+ throw IllegalStateException("$this doesn't have $extra")
+ }
+ }
+ }
+
+ private fun String.toTilesList() = toTilesList(this)
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 47c99f25730b..00ea0b5c5ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -18,34 +18,19 @@ package com.android.systemui.qs.pipeline.data.repository
import android.annotation.UserIdInt
import android.content.res.Resources
-import android.database.ContentObserver
-import android.provider.Settings
import android.util.SparseArray
import com.android.systemui.res.R
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.retail.data.repository.RetailModeRepository
-import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.withContext
/** Repository that tracks the current tiles. */
interface TileSpecRepository {
@@ -55,7 +40,7 @@ interface TileSpecRepository {
*
* Tiles will never be [TileSpec.Invalid] in the list and it will never be empty.
*/
- fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>>
+ suspend fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>>
/**
* Adds a [tile] for a given [userId] at [position]. Using [POSITION_AT_END] will add the tile
@@ -81,6 +66,8 @@ interface TileSpecRepository {
*/
suspend fun setTiles(@UserIdInt userId: Int, tiles: List<TileSpec>)
+ suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>)
+
companion object {
/** Position to indicate the end of the list */
const val POSITION_AT_END = -1
@@ -88,28 +75,23 @@ interface TileSpecRepository {
}
/**
- * Implementation of [TileSpecRepository] that persist the values of tiles in
- * [Settings.Secure.QS_TILES].
- *
- * All operations against [Settings] will be performed in a background thread.
+ * Implementation of [TileSpecRepository] that delegates to an instance of [UserTileSpecRepository]
+ * for each user.
*
* If the device is in retail mode, the tiles are fixed to the value of
* [R.string.quick_settings_tiles_retail_mode].
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class TileSpecSettingsRepository
@Inject
constructor(
- private val secureSettings: SecureSettings,
@Main private val resources: Resources,
private val logger: QSPipelineLogger,
private val retailModeRepository: RetailModeRepository,
- @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val userTileSpecRepositoryFactory: UserTileSpecRepository.Factory,
) : TileSpecRepository {
- private val mutex = Mutex()
- private val tileSpecsPerUser = SparseArray<List<TileSpec>>()
-
private val retailModeTiles by lazy {
resources
.getString(R.string.quick_settings_tiles_retail_mode)
@@ -118,123 +100,59 @@ constructor(
.filter { it !is TileSpec.Invalid }
}
- @OptIn(ExperimentalCoroutinesApi::class)
- override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+ private val userTileRepositories = SparseArray<UserTileSpecRepository>()
+
+ override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+ if (userId !in userTileRepositories) {
+ val userTileRepository = userTileSpecRepositoryFactory.create(userId)
+ userTileRepositories.put(userId, userTileRepository)
+ }
+ val realTiles = userTileRepositories.get(userId).tiles()
+
return retailModeRepository.retailMode.flatMapLatest { inRetailMode ->
if (inRetailMode) {
logger.logUsingRetailTiles()
flowOf(retailModeTiles)
} else {
- settingsTiles(userId)
+ realTiles
}
}
}
- private fun settingsTiles(userId: Int): Flow<List<TileSpec>> {
- return conflatedCallbackFlow {
- val observer =
- object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- trySend(Unit)
- }
- }
-
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
-
- awaitClose { secureSettings.unregisterContentObserver(observer) }
- }
- .onStart { emit(Unit) }
- .map { loadTiles(userId) }
- .onEach { logger.logTilesChangedInSettings(it, userId) }
- .distinctUntilChanged()
- .map { parseTileSpecs(it, userId).also { storeTiles(userId, it) } }
- .distinctUntilChanged()
- .onEach { mutex.withLock { tileSpecsPerUser.put(userId, it) } }
- .flowOn(backgroundDispatcher)
- }
-
- override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) =
- mutex.withLock {
- if (tile == TileSpec.Invalid) {
- return
- }
- val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
- if (tile !in tilesList) {
- if (position < 0 || position >= tilesList.size) {
- tilesList.add(tile)
- } else {
- tilesList.add(position, tile)
- }
- storeTiles(userId, tilesList)
- tileSpecsPerUser.put(userId, tilesList)
- }
- }
-
- override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) =
- mutex.withLock {
- if (tiles.all { it == TileSpec.Invalid }) {
- return
- }
- val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
- if (tilesList.removeAll(tiles)) {
- storeTiles(userId, tilesList.toList())
- tileSpecsPerUser.put(userId, tilesList)
- }
+ override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) {
+ if (retailModeRepository.inRetailMode) {
+ return
}
-
- override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) =
- mutex.withLock {
- val filtered = tiles.filter { it != TileSpec.Invalid }
- if (filtered.isNotEmpty()) {
- storeTiles(userId, filtered)
- tileSpecsPerUser.put(userId, tiles)
- }
+ if (tile is TileSpec.Invalid) {
+ return
}
+ userTileRepositories.get(userId)?.addTile(tile, position)
+ }
- private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
+ override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) {
if (retailModeRepository.inRetailMode) {
- // No storing tiles when in retail mode
return
}
- val toStore =
- tiles
- .filter { it !is TileSpec.Invalid }
- .joinToString(DELIMITER, transform = TileSpec::spec)
- withContext(backgroundDispatcher) {
- secureSettings.putStringForUser(
- SETTING,
- toStore,
- null,
- false,
- forUser,
- true,
- )
- }
+ userTileRepositories.get(userId)?.removeTiles(tiles)
}
- private suspend fun loadTiles(userId: Int): String {
- return withContext(backgroundDispatcher) {
- secureSettings.getStringForUser(SETTING, userId) ?: ""
+ override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) {
+ if (retailModeRepository.inRetailMode) {
+ return
}
+ userTileRepositories.get(userId)?.setTiles(tiles)
}
- private fun parseTileSpecs(tilesFromSettings: String, user: Int): List<TileSpec> {
- val fromSettings =
- tilesFromSettings.split(DELIMITER).map(TileSpec::create).filter {
- it != TileSpec.Invalid
- }
- return if (fromSettings.isNotEmpty()) {
- fromSettings.also { logger.logParsedTiles(it, false, user) }
- } else {
- QSHost.getDefaultSpecs(resources)
- .map(TileSpec::create)
- .filter { it != TileSpec.Invalid }
- .also { logger.logParsedTiles(it, true, user) }
- }
+ override suspend fun reconcileRestore(
+ restoreData: RestoreData,
+ currentAutoAdded: Set<TileSpec>
+ ) {
+ userTileRepositories
+ .get(restoreData.userId)
+ ?.reconcileRestore(restoreData, currentAutoAdded)
}
companion object {
- private const val SETTING = Settings.Secure.QS_TILES
- private const val DELIMITER = ","
+ private const val DELIMITER = TilesSettingConverter.DELIMITER
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt
new file mode 100644
index 000000000000..bb55fcdac58a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt
@@ -0,0 +1,18 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+object TilesSettingConverter {
+
+ const val DELIMITER = ","
+
+ fun toTilesList(commaSeparatedTiles: String) =
+ commaSeparatedTiles.split(DELIMITER).map(TileSpec::create).filter { it != TileSpec.Invalid }
+
+ fun toTilesSet(commaSeparatedTiles: String) =
+ commaSeparatedTiles
+ .split(DELIMITER)
+ .map(TileSpec::create)
+ .filter { it != TileSpec.Invalid }
+ .toSet()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
new file mode 100644
index 000000000000..d452241e86e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
@@ -0,0 +1,186 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.SecureSettings
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Single user version of [AutoAddRepository]. It provides a similar interface as
+ * [AutoAddRepository], but focusing solely on the user it was created for.
+ *
+ * This is the source of truth for that user's tiles, after the user has been started. Persisting
+ * all the changes to [Settings]. Changes in [Settings] that disagree with this repository will be
+ * reverted
+ *
+ * All operations against [Settings] will be performed in a background thread.
+ */
+class UserAutoAddRepository
+@AssistedInject
+constructor(
+ @Assisted private val userId: Int,
+ private val secureSettings: SecureSettings,
+ private val logger: QSPipelineLogger,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+) {
+
+ private val changeEvents = MutableSharedFlow<ChangeAction>(
+ extraBufferCapacity = CHANGES_BUFFER_SIZE
+ )
+
+ private lateinit var _autoAdded: StateFlow<Set<TileSpec>>
+
+ suspend fun autoAdded(): StateFlow<Set<TileSpec>> {
+ if (!::_autoAdded.isInitialized) {
+ _autoAdded =
+ changeEvents
+ .scan(load().also { logger.logAutoAddTilesParsed(userId, it) }) {
+ current,
+ change ->
+ change.apply(current).also {
+ if (change is RestoreTiles) {
+ logger.logAutoAddTilesRestoredReconciled(userId, it)
+ }
+ }
+ }
+ .flowOn(bgDispatcher)
+ .stateIn(applicationScope)
+ .also { startFlowCollections(it) }
+ }
+ return _autoAdded
+ }
+
+ private fun startFlowCollections(autoAdded: StateFlow<Set<TileSpec>>) {
+ applicationScope.launch(bgDispatcher) {
+ launch { autoAdded.collect { store(it) } }
+ launch {
+ // As Settings is not the source of truth, once we started tracking tiles for a
+ // user, we don't want anyone to change the underlying setting. Therefore, if there
+ // are any changes that don't match with the source of truth (this class), we
+ // overwrite them with the current value.
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+ secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserver(observer) }
+ }
+ .map { load() }
+ .flowOn(bgDispatcher)
+ .collect { setting ->
+ val current = autoAdded.value
+ if (setting != current) {
+ store(current)
+ }
+ }
+ }
+ }
+ }
+
+ suspend fun markTileAdded(spec: TileSpec) {
+ if (spec is TileSpec.Invalid) {
+ return
+ }
+ changeEvents.emit(MarkTile(spec))
+ }
+
+ suspend fun unmarkTileAdded(spec: TileSpec) {
+ if (spec is TileSpec.Invalid) {
+ return
+ }
+ changeEvents.emit(UnmarkTile(spec))
+ }
+
+ private suspend fun store(tiles: Set<TileSpec>) {
+ val toStore =
+ tiles
+ .filter { it !is TileSpec.Invalid }
+ .joinToString(DELIMITER, transform = TileSpec::spec)
+ withContext(bgDispatcher) {
+ secureSettings.putStringForUser(
+ SETTING,
+ toStore,
+ null,
+ false,
+ userId,
+ true,
+ )
+ }
+ }
+
+ private suspend fun load(): Set<TileSpec> {
+ return withContext(bgDispatcher) {
+ (secureSettings.getStringForUser(SETTING, userId) ?: "").toTilesSet()
+ }
+ }
+
+ suspend fun reconcileRestore(restoreData: RestoreData) {
+ changeEvents.emit(RestoreTiles(restoreData))
+ }
+
+ private sealed interface ChangeAction {
+ fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec>
+ }
+
+ private data class MarkTile(
+ val tileSpec: TileSpec,
+ ) : ChangeAction {
+ override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+ return currentAutoAdded.toMutableSet().apply { add(tileSpec) }
+ }
+ }
+
+ private data class UnmarkTile(
+ val tileSpec: TileSpec,
+ ) : ChangeAction {
+ override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+ return currentAutoAdded.toMutableSet().apply { remove(tileSpec) }
+ }
+ }
+
+ private data class RestoreTiles(
+ val restoredData: RestoreData,
+ ) : ChangeAction {
+ override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+ return currentAutoAdded + restoredData.restoredAutoAddedTiles
+ }
+ }
+
+ companion object {
+ private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+ private const val DELIMITER = ","
+ // We want a small buffer in case multiple changes come in at the same time (sometimes
+ // happens in first start. This should be enough to not lose changes.
+ private const val CHANGES_BUFFER_SIZE = 10
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(userId: Int): UserAutoAddRepository
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
new file mode 100644
index 000000000000..152fd0f83811
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -0,0 +1,252 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.annotation.UserIdInt
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.SecureSettings
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+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.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Single user version of [TileSpecRepository]. It provides a similar interface as
+ * [TileSpecRepository], but focusing solely on the user it was created for.
+ *
+ * This is the source of truth for that user's tiles, after the user has been started. Persisting
+ * all the changes to [Settings]. Changes in [Settings] that disagree with this repository will be
+ * reverted
+ *
+ * All operations against [Settings] will be performed in a background thread.
+ */
+class UserTileSpecRepository
+@AssistedInject
+constructor(
+ @Assisted private val userId: Int,
+ private val defaultTilesRepository: DefaultTilesRepository,
+ private val secureSettings: SecureSettings,
+ private val logger: QSPipelineLogger,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ private val defaultTiles: List<TileSpec>
+ get() = defaultTilesRepository.defaultTiles
+
+ private val changeEvents = MutableSharedFlow<ChangeAction>(
+ extraBufferCapacity = CHANGES_BUFFER_SIZE
+ )
+
+ private lateinit var _tiles: StateFlow<List<TileSpec>>
+
+ suspend fun tiles(): Flow<List<TileSpec>> {
+ if (!::_tiles.isInitialized) {
+ _tiles =
+ changeEvents
+ .scan(loadTilesFromSettingsAndParse(userId)) { current, change ->
+ change.apply(current).also {
+ if (current != it) {
+ if (change is RestoreTiles) {
+ logger.logTilesRestoredAndReconciled(current, it, userId)
+ } else {
+ logger.logProcessTileChange(change, it, userId)
+ }
+ }
+ }
+ }
+ .flowOn(backgroundDispatcher)
+ .stateIn(applicationScope)
+ .also { startFlowCollections(it) }
+ }
+ return _tiles
+ }
+
+ private fun startFlowCollections(tiles: StateFlow<List<TileSpec>>) {
+ applicationScope.launch(backgroundDispatcher) {
+ launch { tiles.collect { storeTiles(userId, it) } }
+ launch {
+ // As Settings is not the source of truth, once we started tracking tiles for a
+ // user, we don't want anyone to change the underlying setting. Therefore, if there
+ // are any changes that don't match with the source of truth (this class), we
+ // overwrite them with the current value.
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+ secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserver(observer) }
+ }
+ .map { loadTilesFromSettings(userId) }
+ .flowOn(backgroundDispatcher)
+ .collect { setting ->
+ val current = tiles.value
+ if (setting != current) {
+ storeTiles(userId, current)
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
+ val toStore =
+ tiles
+ .filter { it !is TileSpec.Invalid }
+ .joinToString(DELIMITER, transform = TileSpec::spec)
+ withContext(backgroundDispatcher) {
+ secureSettings.putStringForUser(
+ SETTING,
+ toStore,
+ null,
+ false,
+ forUser,
+ true,
+ )
+ }
+ }
+
+ suspend fun addTile(tile: TileSpec, position: Int = TileSpecRepository.POSITION_AT_END) {
+ if (tile is TileSpec.Invalid) {
+ return
+ }
+ changeEvents.emit(AddTile(tile, position))
+ }
+
+ suspend fun removeTiles(tiles: Collection<TileSpec>) {
+ changeEvents.emit(RemoveTiles(tiles))
+ }
+
+ suspend fun setTiles(tiles: List<TileSpec>) {
+ changeEvents.emit(ChangeTiles(tiles))
+ }
+
+ private fun parseTileSpecs(fromSettings: List<TileSpec>, user: Int): List<TileSpec> {
+ return if (fromSettings.isNotEmpty()) {
+ fromSettings.also { logger.logParsedTiles(it, false, user) }
+ } else {
+ defaultTiles.also { logger.logParsedTiles(it, true, user) }
+ }
+ }
+
+ private suspend fun loadTilesFromSettingsAndParse(userId: Int): List<TileSpec> {
+ return parseTileSpecs(loadTilesFromSettings(userId), userId)
+ }
+
+ private suspend fun loadTilesFromSettings(userId: Int): List<TileSpec> {
+ return withContext(backgroundDispatcher) {
+ secureSettings.getStringForUser(SETTING, userId) ?: ""
+ }
+ .toTilesList()
+ }
+
+ suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>) {
+ changeEvents.emit(RestoreTiles(restoreData, currentAutoAdded))
+ }
+
+ sealed interface ChangeAction {
+ fun apply(currentTiles: List<TileSpec>): List<TileSpec>
+ }
+
+ private data class AddTile(
+ val tileSpec: TileSpec,
+ val position: Int = TileSpecRepository.POSITION_AT_END
+ ) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ val tilesList = currentTiles.toMutableList()
+ if (tileSpec !in tilesList) {
+ if (position < 0 || position >= tilesList.size) {
+ tilesList.add(tileSpec)
+ } else {
+ tilesList.add(position, tileSpec)
+ }
+ }
+ return tilesList
+ }
+ }
+
+ private data class RemoveTiles(val tileSpecs: Collection<TileSpec>) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return currentTiles.toMutableList().apply { removeAll(tileSpecs) }
+ }
+ }
+
+ private data class ChangeTiles(
+ val newTiles: List<TileSpec>,
+ ) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ val new = newTiles.filter { it !is TileSpec.Invalid }
+ return if (new.isNotEmpty()) new else currentTiles
+ }
+ }
+
+ private data class RestoreTiles(
+ val restoreData: RestoreData,
+ val currentAutoAdded: Set<TileSpec>,
+ ) : ChangeAction {
+
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return reconcileTiles(currentTiles, currentAutoAdded, restoreData)
+ }
+ }
+
+ companion object {
+ private const val SETTING = Settings.Secure.QS_TILES
+ private const val DELIMITER = TilesSettingConverter.DELIMITER
+ // We want a small buffer in case multiple changes come in at the same time (sometimes
+ // happens in first start. This should be enough to not lose changes.
+ private const val CHANGES_BUFFER_SIZE = 10
+
+ private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+
+ fun reconcileTiles(
+ currentTiles: List<TileSpec>,
+ currentAutoAdded: Set<TileSpec>,
+ restoreData: RestoreData
+ ): List<TileSpec> {
+ val toRestore = restoreData.restoredTiles.toMutableList()
+ val freshlyAutoAdded =
+ currentAutoAdded.filterNot { it in restoreData.restoredAutoAddedTiles }
+ freshlyAutoAdded
+ .filter { it in currentTiles && it !in restoreData.restoredTiles }
+ .map { it to currentTiles.indexOf(it) }
+ .sortedBy { it.second }
+ .forEachIndexed { iteration, (tile, position) ->
+ val insertAt = position + iteration
+ if (insertAt > toRestore.size) {
+ toRestore.add(tile)
+ } else {
+ toRestore.add(insertAt, tile)
+ }
+ }
+
+ return toRestore
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ userId: Int,
+ ): UserTileSpecRepository
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 5a5e47af73c9..c5512c15ccc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -20,6 +20,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.UserHandle
+import android.util.Log
import com.android.systemui.Dumpable
import com.android.systemui.ProtoDumpable
import com.android.systemui.dagger.SysUISingleton
@@ -40,10 +41,12 @@ import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
+import dagger.Lazy
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -130,6 +133,7 @@ constructor(
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userRepository: UserRepository,
private val customTileStatePersister: CustomTileStatePersister,
+ private val newQSTileFactory: Lazy<NewQSTileFactory>,
private val tileFactory: QSFactory,
private val customTileAddedRepository: CustomTileAddedRepository,
private val tileLifecycleManagerFactory: TileLifecycleManager.Factory,
@@ -138,7 +142,7 @@ constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
private val logger: QSPipelineLogger,
- featureFlags: QSPipelineFlagsRepository,
+ private val featureFlags: QSPipelineFlagsRepository,
) : CurrentTilesInteractor {
private val _currentSpecsAndTiles: MutableStateFlow<List<TileModel>> =
@@ -268,6 +272,7 @@ constructor(
// repository
launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
}
+ Log.d("Fabian", "Finished resolving tiles")
}
}
}
@@ -331,12 +336,19 @@ constructor(
}
private suspend fun createTile(spec: TileSpec): QSTile? {
- val tile = withContext(mainDispatcher) { tileFactory.createTile(spec.spec) }
+ val tile =
+ withContext(mainDispatcher) {
+ if (featureFlags.pipelineTilesEnabled) {
+ newQSTileFactory.get().createTile(spec.spec)
+ } else {
+ null
+ }
+ ?: tileFactory.createTile(spec.spec)
+ }
if (tile == null) {
logger.logTileNotFoundInFactory(spec)
return null
} else {
- tile.tileSpec = spec.spec
return if (!tile.isAvailable) {
logger.logTileDestroyed(
spec,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
new file mode 100644
index 000000000000..9844903eff26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
@@ -0,0 +1,53 @@
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.launch
+
+/**
+ * Interactor in charge of triggering reconciliation after QS Secure Settings are restored. For a
+ * given user, it will trigger the reconciliations in the correct order to prevent race conditions.
+ *
+ * Currently, the order is:
+ * 1. TileSpecRepository, with the restored data and the current (before restore) auto add tiles
+ * 2. AutoAddRepository
+ *
+ * [start] needs to be called to trigger the collection of [QSSettingsRestoredRepository].
+ */
+@SysUISingleton
+class RestoreReconciliationInteractor
+@Inject
+constructor(
+ private val tileSpecRepository: TileSpecRepository,
+ private val autoAddRepository: AutoAddRepository,
+ private val qsSettingsRestoredRepository: QSSettingsRestoredRepository,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ fun start() {
+ applicationScope.launch(backgroundDispatcher) {
+ qsSettingsRestoredRepository.restoreData.flatMapConcat { data ->
+ autoAddRepository.autoAddedTiles(data.userId)
+ .take(1)
+ .map { tiles -> data to tiles }
+ }.collect { (restoreData, autoAdded) ->
+ tileSpecRepository.reconcileRestore(restoreData, autoAdded)
+ autoAddRepository.reconcileRestore(restoreData)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index 0743ba0b121a..1539f05508d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -20,6 +20,7 @@ import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractor
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import javax.inject.Inject
@@ -30,11 +31,13 @@ constructor(
private val currentTilesInteractor: CurrentTilesInteractor,
private val autoAddInteractor: AutoAddInteractor,
private val featureFlags: QSPipelineFlagsRepository,
+ private val restoreReconciliationInteractor: RestoreReconciliationInteractor,
) : CoreStartable {
override fun start() {
if (featureFlags.pipelineAutoAddEnabled) {
autoAddInteractor.init(currentTilesInteractor)
+ restoreReconciliationInteractor.start()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 551b0f4890a4..1a71b715fe3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -1,7 +1,7 @@
package com.android.systemui.qs.pipeline.shared
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import javax.inject.Inject
@@ -10,7 +10,7 @@ import javax.inject.Inject
class QSPipelineFlagsRepository
@Inject
constructor(
- private val featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlagsClassic,
) {
/** @see Flags.QS_PIPELINE_NEW_HOST */
@@ -20,4 +20,8 @@ constructor(
/** @see Flags.QS_PIPELINE_AUTO_ADD */
val pipelineAutoAddEnabled: Boolean
get() = pipelineHostEnabled && featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
+
+ /** @see Flags.QS_PIPELINE_NEW_TILES */
+ val pipelineTilesEnabled: Boolean
+ get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_TILES)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index 11b5dd7cb036..aed08f8b5457 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -31,11 +31,7 @@ import com.android.systemui.qs.external.CustomTile
sealed class TileSpec private constructor(open val spec: String) {
/** Represents a spec that couldn't be parsed into a valid type of tile. */
- object Invalid : TileSpec("") {
- override fun toString(): String {
- return "TileSpec.INVALID"
- }
- }
+ data object Invalid : TileSpec("")
/** Container for the spec of a tile provided by SystemUI. */
data class PlatformTileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index 573cb7154c4f..bca86c9ee8af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -16,11 +16,13 @@
package com.android.systemui.qs.pipeline.shared.logging
-import android.annotation.UserIdInt
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog
+import com.android.systemui.qs.pipeline.dagger.QSRestoreLog
import com.android.systemui.qs.pipeline.dagger.QSTileListLog
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.UserTileSpecRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
@@ -34,11 +36,13 @@ class QSPipelineLogger
constructor(
@QSTileListLog private val tileListLogBuffer: LogBuffer,
@QSAutoAddLog private val tileAutoAddLogBuffer: LogBuffer,
+ @QSRestoreLog private val restoreLogBuffer: LogBuffer,
) {
companion object {
const val TILE_LIST_TAG = "QSTileListLog"
const val AUTO_ADD_TAG = "QSAutoAddableLog"
+ const val RESTORE_TAG = "QSRestoreLog"
}
/**
@@ -60,20 +64,37 @@ constructor(
)
}
- /**
- * Logs when the tiles change in Settings.
- *
- * This could be caused by SystemUI, or restore.
- */
- fun logTilesChangedInSettings(newTiles: String, @UserIdInt user: Int) {
+ fun logTilesRestoredAndReconciled(
+ currentTiles: List<TileSpec>,
+ reconciledTiles: List<TileSpec>,
+ user: Int,
+ ) {
tileListLogBuffer.log(
TILE_LIST_TAG,
- LogLevel.VERBOSE,
+ LogLevel.DEBUG,
{
- str1 = newTiles
+ str1 = currentTiles.toString()
+ str2 = reconciledTiles.toString()
int1 = user
},
- { "Tiles changed in settings for user $int1: $str1" }
+ { "Tiles restored and reconciled for user: $int1\nWas: $str1\nSet to: $str2" }
+ )
+ }
+
+ fun logProcessTileChange(
+ action: UserTileSpecRepository.ChangeAction,
+ newList: List<TileSpec>,
+ userId: Int,
+ ) {
+ tileListLogBuffer.log(
+ TILE_LIST_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = action.toString()
+ str2 = newList.toString()
+ int1 = userId
+ },
+ { "Processing $str1 for user $int1\nNew list: $str2" }
)
}
@@ -139,6 +160,30 @@ constructor(
)
}
+ fun logAutoAddTilesParsed(userId: Int, tiles: Set<TileSpec>) {
+ tileAutoAddLogBuffer.log(
+ AUTO_ADD_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = tiles.toString()
+ int1 = userId
+ },
+ { "Auto add tiles parsed for user $int1: $str1" }
+ )
+ }
+
+ fun logAutoAddTilesRestoredReconciled(userId: Int, tiles: Set<TileSpec>) {
+ tileAutoAddLogBuffer.log(
+ AUTO_ADD_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = tiles.toString()
+ int1 = userId
+ },
+ { "Auto-add tiles reconciled for user $int1: $str1" }
+ )
+ }
+
fun logTileAutoAdded(userId: Int, spec: TileSpec, position: Int) {
tileAutoAddLogBuffer.log(
AUTO_ADD_TAG,
@@ -164,6 +209,23 @@ constructor(
)
}
+ fun logSettingsRestored(restoreData: RestoreData) {
+ restoreLogBuffer.log(
+ RESTORE_TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = restoreData.userId
+ str1 = restoreData.restoredTiles.toString()
+ str2 = restoreData.restoredAutoAddedTiles.toString()
+ },
+ {
+ "Restored settings data for user $int1\n" +
+ "\tRestored tiles: $str1\n" +
+ "\tRestored auto added tiles: $str2"
+ }
+ )
+ }
+
/** Reasons for destroying an existing tile. */
enum class TileDestroyedReason(val readable: String) {
TILE_REMOVED("Tile removed from current set"),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9c7a73412518..632aa6330a18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -70,6 +70,7 @@ public class QSFactoryImpl implements QSFactory {
if (tile != null) {
tile.initialize();
tile.postStale(); // Tile was just created, must be stale.
+ tile.setTileSpec(tileSpec);
}
return tile;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index ea162fa2921f..5a9500494de0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -133,7 +133,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements
@Override
protected void handleClick(@Nullable View view) {
final boolean newState = !mState.value;
- mController.setRotationLocked(!newState);
+ mController.setRotationLocked(!newState, /* caller= */ "RotationLockTile#handleClick");
refreshState(newState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
index e9f907c4d8e7..dc9e11567676 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
@@ -1,11 +1,11 @@
package com.android.systemui.qs.tiles.base.actions
import android.content.Intent
+import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/**
@@ -17,9 +17,9 @@ class QSTileIntentUserActionHandler
@Inject
constructor(private val activityStarter: ActivityStarter) {
- fun handle(userAction: QSTileUserAction, intent: Intent) {
+ fun handle(view: View?, intent: Intent) {
val animationController: ActivityLaunchAnimator.Controller? =
- userAction.view?.let {
+ view?.let {
ActivityLaunchAnimator.Controller.fromView(
it,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
index c2a75fac60f5..bb4de808de79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
@@ -92,7 +92,7 @@ constructor(
.stateIn(
tileScope,
SharingStarted.WhileSubscribed(),
- false,
+ true,
)
private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
new file mode 100644
index 000000000000..3fedbfc6671d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -0,0 +1,28 @@
+package com.android.systemui.qs.tiles.di
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
+import javax.inject.Inject
+import javax.inject.Provider
+
+// TODO(b/http://b/299909989): Rename the factory after rollout
+@SysUISingleton
+class NewQSTileFactory
+@Inject
+constructor(
+ private val adapterFactory: QSTileViewModelAdapter.Factory,
+ private val tileMap:
+ Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
+) : QSFactory {
+
+ override fun createTile(tileSpec: String): QSTile? =
+ tileMap[tileSpec]?.let {
+ val tile = it.get()
+ tile.onLifecycle(QSTileLifecycle.ALIVE)
+ adapterFactory.create(tile)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index a5eaac154230..019d3c0ee416 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -1,14 +1,13 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import androidx.annotation.StringRes
+import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
data class QSTileConfig(
val tileSpec: TileSpec,
val tileIcon: Icon,
- val tileLabel: CharSequence,
-// TODO(b/299908705): Fill necessary params
-/*
-val instanceId: InstanceId,
- */
+ @StringRes val tileLabelRes: Int,
+ val instanceId: InstanceId,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 53f9edfb954c..dc5c69080fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -1,18 +1,96 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import android.service.quicksettings.Tile
+import com.android.systemui.common.shared.model.Icon
+/**
+ * Represents current a state of the tile to be displayed in on the view. Consider using
+ * [QSTileState.build] for better state creation experience and preset default values for certain
+ * fields.
+ *
+ * // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
+ */
data class QSTileState(
- val icon: Icon,
+ val icon: () -> Icon,
val label: CharSequence,
-// TODO(b/299908705): Fill necessary params
-/*
- val subtitle: CharSequence = "",
- val activeState: ActivationState = Active,
- val enabledState: Enabled = Enabled,
- val loopIconAnimation: Boolean = false,
- val secondaryIcon: Icon? = null,
- val slashState: SlashState? = null,
- val supportedActions: Collection<UserAction> = listOf(Click), clicks should be a default action
-*/
-)
+ val activationState: ActivationState,
+ val secondaryLabel: CharSequence?,
+ val supportedActions: Set<UserAction>,
+ val contentDescription: CharSequence?,
+ val stateDescription: CharSequence?,
+ val sideViewIcon: SideViewIcon,
+ val enabledState: EnabledState,
+ val expandedAccessibilityClassName: String?,
+) {
+
+ companion object {
+
+ fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+ Builder(icon, label).apply(build).build()
+
+ fun build(icon: Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+ build({ icon }, label, build)
+ }
+
+ enum class ActivationState(val legacyState: Int) {
+ // An unavailable state indicates that for some reason this tile is not currently available
+ // to the user, and will have no click action. The tile's icon will be tinted differently to
+ // reflect this state.
+ UNAVAILABLE(Tile.STATE_UNAVAILABLE),
+ // This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is
+ // on, cast is casting). This is the default state.
+ ACTIVE(Tile.STATE_ACTIVE),
+ // This represents a tile that is currently in a disabled state but is still interactable. A
+ // disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
+ // bluetooth disabled), but is still interactable by the user to modify this state.
+ INACTIVE(Tile.STATE_INACTIVE),
+ }
+
+ /**
+ * Enabled tile behaves as usual where is disabled one is frozen and inactive in its current
+ * [ActivationState].
+ */
+ enum class EnabledState {
+ ENABLED,
+ DISABLED,
+ }
+
+ enum class UserAction {
+ CLICK,
+ LONG_CLICK,
+ }
+
+ sealed interface SideViewIcon {
+ data class Custom(val icon: Icon) : SideViewIcon
+ data object Chevron : SideViewIcon
+ data object None : SideViewIcon
+ }
+
+ class Builder(
+ var icon: () -> Icon,
+ var label: CharSequence,
+ ) {
+ var activationState: ActivationState = ActivationState.INACTIVE
+ var secondaryLabel: CharSequence? = null
+ var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
+ var contentDescription: CharSequence? = null
+ var stateDescription: CharSequence? = null
+ var sideViewIcon: SideViewIcon = SideViewIcon.None
+ var enabledState: EnabledState = EnabledState.ENABLED
+ var expandedAccessibilityClassName: String? = null
+
+ fun build(): QSTileState =
+ QSTileState(
+ icon,
+ label,
+ activationState,
+ secondaryLabel,
+ supportedActions,
+ contentDescription,
+ stateDescription,
+ sideViewIcon,
+ enabledState,
+ expandedAccessibilityClassName,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
index f1f8f0152c67..0b232c28d3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
@@ -1,13 +1,11 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.content.Context
import android.view.View
sealed interface QSTileUserAction {
- val context: Context
val view: View?
- class Click(override val context: Context, override val view: View?) : QSTileUserAction
- class LongClick(override val context: Context, override val view: View?) : QSTileUserAction
+ class Click(override val view: View?) : QSTileUserAction
+ class LongClick(override val view: View?) : QSTileUserAction
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
new file mode 100644
index 000000000000..d4bdb77c2b41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -0,0 +1,232 @@
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.content.Context
+import android.util.Log
+import android.view.View
+import androidx.annotation.GuardedBy
+import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collectIndexed
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+// TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
+class QSTileViewModelAdapter
+@AssistedInject
+constructor(
+ private val qsHost: QSHost,
+ @Assisted private val qsTileViewModel: QSTileViewModel,
+) : QSTile {
+
+ private val context
+ get() = qsHost.context
+
+ @GuardedBy("callbacks")
+ private val callbacks: MutableCollection<QSTile.Callback> = mutableSetOf()
+ @GuardedBy("listeningClients")
+ private val listeningClients: MutableCollection<Any> = mutableSetOf()
+
+ // Cancels the jobs when the adapter is no longer alive
+ private val adapterScope = CoroutineScope(SupervisorJob())
+ // Cancels the jobs when clients stop listening
+ private val listeningScope = CoroutineScope(SupervisorJob())
+
+ init {
+ adapterScope.launch {
+ qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
+ if (!isAvailable) {
+ qsHost.removeTile(tileSpec)
+ }
+ // qsTileViewModel.isAvailable flow often starts with isAvailable == true. That's
+ // why we only allow isAvailable == true once and throw an exception afterwards.
+ if (index > 0 && isAvailable) {
+ // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for additional
+ // guidance on how to auto add your tile
+ throw UnsupportedOperationException("Turning on tile is not supported now")
+ }
+ }
+ }
+
+ // QSTileHost doesn't call this when userId is initialized
+ userSwitch(qsHost.userId)
+
+ if (DEBUG) {
+ Log.d(TAG, "Using new tiles for: $tileSpec")
+ }
+ }
+
+ override fun isAvailable(): Boolean = qsTileViewModel.isAvailable.value
+
+ override fun setTileSpec(tileSpec: String?) {
+ throw UnsupportedOperationException("Tile spec is immutable in new tiles")
+ }
+
+ override fun refreshState() {
+ qsTileViewModel.forceUpdate()
+ }
+
+ override fun addCallback(callback: QSTile.Callback?) {
+ callback ?: return
+ synchronized(callbacks) { callbacks.add(callback) }
+ }
+
+ override fun removeCallback(callback: QSTile.Callback?) {
+ callback ?: return
+ synchronized(callbacks) { callbacks.remove(callback) }
+ }
+
+ override fun removeCallbacks() {
+ synchronized(callbacks) { callbacks.clear() }
+ }
+
+ override fun click(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+ }
+ }
+
+ override fun secondaryClick(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+ }
+ }
+
+ override fun longClick(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.LONG_CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.LongClick(view))
+ }
+ }
+
+ private fun isActionSupported(action: QSTileState.UserAction): Boolean =
+ qsTileViewModel.currentState?.supportedActions?.contains(action) == true
+
+ override fun userSwitch(currentUser: Int) {
+ qsTileViewModel.onUserIdChanged(currentUser)
+ }
+
+ @Deprecated(
+ "Not needed as {@link com.android.internal.logging.UiEvent} will use #getMetricsSpec",
+ replaceWith = ReplaceWith("getMetricsSpec"),
+ )
+ override fun getMetricsCategory(): Int = 0
+
+ override fun setListening(client: Any?, listening: Boolean) {
+ client ?: return
+ synchronized(listeningClients) {
+ if (listening) {
+ listeningClients.add(client)
+ if (listeningClients.size == 1) {
+ qsTileViewModel.state
+ .map { mapState(context, it, qsTileViewModel.config) }
+ .onEach { legacyState ->
+ synchronized(callbacks) {
+ callbacks.forEach { it.onStateChanged(legacyState) }
+ }
+ }
+ .launchIn(listeningScope)
+ }
+ } else {
+ listeningClients.remove(client)
+ if (listeningClients.isEmpty()) {
+ listeningScope.coroutineContext.cancelChildren()
+ }
+ }
+ }
+ }
+
+ override fun isListening(): Boolean =
+ synchronized(listeningClients) { listeningClients.isNotEmpty() }
+
+ override fun setDetailListening(show: Boolean) {
+ // do nothing like QSTileImpl
+ }
+
+ override fun destroy() {
+ adapterScope.cancel()
+ listeningScope.cancel()
+ qsTileViewModel.onLifecycle(QSTileLifecycle.DEAD)
+ }
+
+ override fun getState(): QSTile.State? =
+ qsTileViewModel.currentState?.let { mapState(context, it, qsTileViewModel.config) }
+
+ override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
+ override fun getTileLabel(): CharSequence =
+ context.getString(qsTileViewModel.config.tileLabelRes)
+ override fun getTileSpec(): String = qsTileViewModel.config.tileSpec.spec
+
+ private companion object {
+
+ const val DEBUG = false
+ const val TAG = "QSTileVMAdapter"
+
+ fun mapState(
+ context: Context,
+ viewModelState: QSTileState,
+ config: QSTileConfig
+ ): QSTile.State =
+ // we have to use QSTile.BooleanState to support different side icons
+ // which are bound to instanceof QSTile.BooleanState in QSTileView.
+ QSTile.BooleanState().apply {
+ spec = config.tileSpec.spec
+ label = viewModelState.label
+ // This value is synthetic and doesn't have any meaning
+ value = false
+
+ secondaryLabel = viewModelState.secondaryLabel
+ handlesLongClick =
+ viewModelState.supportedActions.contains(QSTileState.UserAction.LONG_CLICK)
+
+ iconSupplier = Supplier {
+ when (val stateIcon = viewModelState.icon()) {
+ is Icon.Loaded -> DrawableIcon(stateIcon.drawable)
+ is Icon.Resource -> ResourceIcon.get(stateIcon.res)
+ }
+ }
+ state = viewModelState.activationState.legacyState
+
+ contentDescription = viewModelState.contentDescription
+ stateDescription = viewModelState.stateDescription
+
+ disabledByPolicy = viewModelState.enabledState == QSTileState.EnabledState.DISABLED
+ expandedAccessibilityClassName = viewModelState.expandedAccessibilityClassName
+
+ when (viewModelState.sideViewIcon) {
+ is QSTileState.SideViewIcon.Custom -> {
+ sideViewCustomDrawable =
+ when (viewModelState.sideViewIcon.icon) {
+ is Icon.Loaded -> viewModelState.sideViewIcon.icon.drawable
+ is Icon.Resource ->
+ context.getDrawable(viewModelState.sideViewIcon.icon.res)
+ }
+ }
+ is QSTileState.SideViewIcon.Chevron -> {
+ forceExpandIcon = true
+ }
+ is QSTileState.SideViewIcon.None -> {
+ forceExpandIcon = false
+ }
+ }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+
+ fun create(qsTileViewModel: QSTileViewModel): QSTileViewModelAdapter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 7cdb6553c47e..3501b6bc045e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -41,10 +41,10 @@ import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
+import com.android.systemui.res.R;
import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index b80a01212ca0..3aab3bf62809 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -51,7 +51,7 @@ import android.util.Size;
import android.view.Surface;
import android.view.WindowManager;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import java.io.Closeable;
import java.io.File;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f0ce8a465d0d..f2e94e94757f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,7 @@ package com.android.systemui.screenrecord;
import static android.app.Activity.RESULT_OK;
-import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
+import static com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
@@ -41,8 +41,8 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import com.android.systemui.res.R;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.SystemUIDialog;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index b5b7043e37a0..a1d5d98ba9e9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -32,10 +32,14 @@ import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Switch
import androidx.annotation.LayoutRes
-import com.android.systemui.res.R
-import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.media.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
+import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
+import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
+import com.android.systemui.mediaprojection.permission.SINGLE_APP
+import com.android.systemui.mediaprojection.permission.ScreenShareOption
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
/** Dialog to select screen recording options */
@@ -58,6 +62,7 @@ class ScreenRecordPermissionDialog(
private lateinit var tapsView: View
private lateinit var audioSwitch: Switch
private lateinit var options: Spinner
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setDialogTitle(R.string.screenrecord_permission_dialog_title)
@@ -177,6 +182,7 @@ class ScreenRecordPermissionDialog(
)
private const val DELAY_MS: Long = 3000
private const val INTERVAL_MS: Long = 1000
+
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index f08eb143efb4..4b3bd0b44bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -53,9 +53,6 @@ import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
-import android.media.AudioAttributes;
-import android.media.AudioSystem;
-import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Process;
@@ -86,8 +83,6 @@ import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.WindowContext;
-import androidx.concurrent.futures.CallbackToFutureAdapter;
-
import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
@@ -108,7 +103,6 @@ import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
-import java.io.File;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@@ -116,11 +110,11 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import javax.inject.Provider;
+
/**
* Controls the state and flow for screenshots.
@@ -274,7 +268,8 @@ public class ScreenshotController {
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private final AccessibilityManager mAccessibilityManager;
- private final ListenableFuture<MediaPlayer> mCameraSound;
+ @Nullable
+ private final ScreenshotSoundController mScreenshotSoundController;
private final ScrollCaptureClient mScrollCaptureClient;
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
@@ -339,6 +334,7 @@ public class ScreenshotController {
UserManager userManager,
AssistContentRequester assistContentRequester,
MessageContainerController messageContainerController,
+ Provider<ScreenshotSoundController> screenshotSoundController,
@Assisted int displayId
) {
mScreenshotSmartActions = screenshotSmartActions;
@@ -387,8 +383,12 @@ public class ScreenshotController {
mConfigChanges.applyNewConfig(context.getResources());
reloadAssets();
- // Setup the Camera shutter sound
- mCameraSound = loadCameraSound();
+ // Sound is only reproduced from the controller of the default display.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mScreenshotSoundController = screenshotSoundController.get();
+ } else {
+ mScreenshotSoundController = null;
+ }
mCopyBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -573,17 +573,8 @@ public class ScreenshotController {
}
private void releaseMediaPlayer() {
- // Note that this may block if the sound is still being loaded (very unlikely) but we can't
- // reliably release in the background because the service is being destroyed.
- try {
- MediaPlayer player = mCameraSound.get(1, TimeUnit.SECONDS);
- if (player != null) {
- player.release();
- }
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- mCameraSound.cancel(true);
- Log.w(TAG, "Error releasing shutter sound", e);
- }
+ if (mScreenshotSoundController == null) return;
+ mScreenshotSoundController.releaseScreenshotSound();
}
private void respondToKeyDismissal() {
@@ -889,39 +880,10 @@ public class ScreenshotController {
}
}
- private ListenableFuture<MediaPlayer> loadCameraSound() {
- // The media player creation is slow and needs on the background thread.
- return CallbackToFutureAdapter.getFuture((completer) -> {
- mBgExecutor.execute(() -> {
- try {
- MediaPlayer player = MediaPlayer.create(mContext,
- Uri.fromFile(new File(mContext.getResources().getString(
- com.android.internal.R.string.config_cameraShutterSound))),
- null,
- new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .build(), AudioSystem.newAudioSessionId());
- completer.set(player);
- } catch (IllegalStateException e) {
- Log.w(TAG, "Screenshot sound initialization failed", e);
- completer.set(null);
- }
- });
- return "ScreenshotController#loadCameraSound";
- });
- }
-
- private void playCameraSound() {
- mCameraSound.addListener(() -> {
- try {
- MediaPlayer player = mCameraSound.get();
- if (player != null) {
- player.start();
- }
- } catch (InterruptedException | ExecutionException e) {
- }
- }, mBgExecutor);
+ private void playCameraSoundIfNeeded() {
+ if (mScreenshotSoundController == null) return;
+ // the controller is not-null only on the default display controller
+ mScreenshotSoundController.playCameraSound();
}
/**
@@ -930,7 +892,7 @@ public class ScreenshotController {
*/
private void saveScreenshotAndToast(UserHandle owner, Consumer<Uri> finisher) {
// Play the shutter sound to notify that we've taken a screenshot
- playCameraSound();
+ playCameraSoundIfNeeded();
saveScreenshotInWorkerThread(
owner,
@@ -974,7 +936,7 @@ public class ScreenshotController {
}
// Play the shutter sound to notify that we've taken a screenshot
- playCameraSound();
+ playCameraSoundIfNeeded();
if (DEBUG_ANIM) {
Log.d(TAG, "starting post-screenshot animation");
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
new file mode 100644
index 000000000000..cd0cab556c4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.media.MediaPlayer
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.TraceUtils.Companion.tracedAsync
+import com.google.errorprone.annotations.CanIgnoreReturnValue
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.withTimeout
+
+/** Controls sound reproduction after a screenshot is taken. */
+interface ScreenshotSoundController {
+ /** Reproduces the camera sound. */
+ @CanIgnoreReturnValue fun playCameraSound(): Deferred<Unit>
+
+ /** Releases the sound. [playCameraSound] behaviour is undefined after this has been called. */
+ @CanIgnoreReturnValue fun releaseScreenshotSound(): Deferred<Unit>
+}
+
+class ScreenshotSoundControllerImpl
+@Inject
+constructor(
+ private val soundProvider: ScreenshotSoundProvider,
+ @Application private val coroutineScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher
+) : ScreenshotSoundController {
+
+ val player: Deferred<MediaPlayer?> =
+ coroutineScope.tracedAsync("loadCameraSound", bgDispatcher) {
+ try {
+ soundProvider.getScreenshotSound()
+ } catch (e: IllegalStateException) {
+ Log.w(TAG, "Screenshot sound initialization failed", e)
+ null
+ }
+ }
+
+ override fun playCameraSound(): Deferred<Unit> {
+ return coroutineScope.tracedAsync("playCameraSound", bgDispatcher) {
+ player.await()?.start()
+ }
+ }
+ override fun releaseScreenshotSound(): Deferred<Unit> {
+ return coroutineScope.tracedAsync("releaseScreenshotSound", bgDispatcher) {
+ try {
+ withTimeout(1.seconds) { player.await()?.release() }
+ } catch (e: TimeoutCancellationException) {
+ player.cancel()
+ Log.w(TAG, "Error releasing shutter sound", e)
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "ScreenshotSoundControllerImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt
new file mode 100644
index 000000000000..73151d5298c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.Context
+import android.media.AudioAttributes
+import android.media.AudioSystem
+import android.media.MediaPlayer
+import android.net.Uri
+import com.android.internal.R
+import com.android.systemui.dagger.SysUISingleton
+import java.io.File
+import javax.inject.Inject
+
+/** Provides a [MediaPlayer] that reproduces the screenshot sound. */
+interface ScreenshotSoundProvider {
+
+ /**
+ * Creates a new [MediaPlayer] that reproduces the screenshot sound. This should be called from
+ * a background thread, as it might take time.
+ */
+ fun getScreenshotSound(): MediaPlayer
+}
+
+@SysUISingleton
+class ScreenshotSoundProviderImpl
+@Inject
+constructor(
+ private val context: Context,
+) : ScreenshotSoundProvider {
+ override fun getScreenshotSound(): MediaPlayer {
+ return MediaPlayer.create(
+ context,
+ Uri.fromFile(File(context.resources.getString(R.string.config_cameraShutterSound))),
+ /* holder = */ null,
+ AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build(),
+ AudioSystem.newAudioSessionId()
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 7d17d4c72b76..3797b8b41e5a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -25,6 +25,10 @@ import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
import com.android.systemui.screenshot.ScreenshotProxyService;
import com.android.systemui.screenshot.ScreenshotRequestProcessor;
+import com.android.systemui.screenshot.ScreenshotSoundController;
+import com.android.systemui.screenshot.ScreenshotSoundControllerImpl;
+import com.android.systemui.screenshot.ScreenshotSoundProvider;
+import com.android.systemui.screenshot.ScreenshotSoundProviderImpl;
import com.android.systemui.screenshot.TakeScreenshotService;
import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
import com.android.systemui.screenshot.appclips.AppClipsService;
@@ -69,4 +73,12 @@ public abstract class ScreenshotModule {
@Binds
abstract ScreenshotRequestProcessor bindScreenshotRequestProcessor(
RequestProcessor requestProcessor);
+
+ @Binds
+ abstract ScreenshotSoundProvider bindScreenshotSoundProvider(
+ ScreenshotSoundProviderImpl screenshotSoundProviderImpl);
+
+ @Binds
+ abstract ScreenshotSoundController bindScreenshotSoundController(
+ ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index b7152370624e..6fa592c6dd78 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,12 +30,13 @@ import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -283,6 +284,7 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
private final VibratorHelper mVibratorHelper;
private final SystemClock mSystemClock;
private final CoroutineDispatcher mMainDispatcher;
+ private final ActivityStarter mActivityStarter;
@Inject
public Factory(
@@ -291,14 +293,15 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
VibratorHelper vibratorHelper,
SystemClock clock,
FeatureFlagsClassic featureFlags,
- @Main CoroutineDispatcher mainDispatcher
- ) {
+ @Main CoroutineDispatcher mainDispatcher,
+ ActivityStarter activityStarter) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mFeatureFlags = featureFlags;
mVibratorHelper = vibratorHelper;
mSystemClock = clock;
mMainDispatcher = mainDispatcher;
+ mActivityStarter = activityStarter;
}
/**
@@ -314,6 +317,8 @@ public class BrightnessSliderController extends ViewController<BrightnessSliderV
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
+ root.setActivityStarter(mActivityStarter);
+
BrightnessSliderHapticPlugin plugin;
if (mFeatureFlags.isEnabled(HAPTIC_BRIGHTNESS_SLIDER)) {
plugin = new BrightnessSliderHapticPluginImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index c88549224183..5ecf07f5a264 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -33,6 +33,7 @@ import androidx.annotation.Nullable;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
/**
@@ -41,6 +42,7 @@ import com.android.systemui.res.R;
*/
public class BrightnessSliderView extends FrameLayout {
+ private ActivityStarter mActivityStarter;
@NonNull
private ToggleSeekBar mSlider;
private DispatchTouchEventListener mListener;
@@ -57,6 +59,10 @@ public class BrightnessSliderView extends FrameLayout {
super(context, attrs);
}
+ public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
+
// Inflated from quick_settings_brightness_dialog
@Override
protected void onFinishInflate() {
@@ -65,6 +71,7 @@ public class BrightnessSliderView extends FrameLayout {
mSlider = requireViewById(R.id.slider);
mSlider.setAccessibilityLabel(getContentDescription().toString());
+ mSlider.setActivityStarter(mActivityStarter);
// Finds the progress drawable. Assumes brightness_progress_drawable.xml
try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index a5a0ae70045e..6ec10da28000 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -23,8 +23,9 @@ import android.view.MotionEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
public class ToggleSeekBar extends SeekBar {
@@ -32,6 +33,8 @@ public class ToggleSeekBar extends SeekBar {
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin = null;
+ private ActivityStarter mActivityStarter;
+
public ToggleSeekBar(Context context) {
super(context);
}
@@ -49,7 +52,7 @@ public class ToggleSeekBar extends SeekBar {
if (mEnforcedAdmin != null) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
return true;
}
if (!isEnabled()) {
@@ -74,4 +77,8 @@ public class ToggleSeekBar extends SeekBar {
public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
mEnforcedAdmin = admin;
}
+
+ public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index e8be40eaf3fe..9b74ac4afed4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -791,7 +791,8 @@ public class QuickSettingsController implements Dumpable {
/** update Qs height state */
public void setExpansionHeight(float height) {
// TODO(b/277909752): remove below log when bug is fixed
- if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0) {
+ if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0
+ && mBarState == SHADE) {
Log.wtf(TAG,
"setting QS height to 0 in split shade while shade is open(ing). "
+ "Value of mExpandImmediate = " + mExpandImmediate);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 251cc168161c..ac8333ae84ad 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -129,6 +129,9 @@ constructor(
combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
+ /** Whether either the shade or QS is fully expanded. */
+ val isAnyFullyExpanded: Flow<Boolean> = anyExpansion.map { it >= 1f }.distinctUntilChanged()
+
/** Whether either the shade or QS is expanding from a fully collapsed state. */
val isAnyExpanding: Flow<Boolean> =
anyExpansion
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 670fb1289357..93bb4356a1d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -172,6 +172,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_LOCK_TASK_MODE_CHANGED = 75 << MSG_SHIFT;
private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;
private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
+ private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -301,6 +302,8 @@ public class CommandQueue extends IStatusBar.Stub implements
default void addQsTile(ComponentName tile) { }
default void remQsTile(ComponentName tile) { }
+
+ default void setQsTiles(String[] tiles) {}
default void clickTile(ComponentName tile) { }
default void handleSystemKey(KeyEvent arg1) { }
@@ -903,6 +906,13 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
+ public void setQsTiles(String[] tiles) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SET_QS_TILES, tiles).sendToTarget();
+ }
+ }
+
+ @Override
public void clickQsTile(ComponentName tile) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_CLICK_QS_TILE, tile).sendToTarget();
@@ -1533,6 +1543,11 @@ public class CommandQueue extends IStatusBar.Stub implements
mCallbacks.get(i).remQsTile((ComponentName) msg.obj);
}
break;
+ case MSG_SET_QS_TILES:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setQsTiles((String[]) msg.obj);
+ }
+ break;
case MSG_CLICK_QS_TILE:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).clickTile((ComponentName) msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8baab25e5c59..f616b91c4712 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -244,12 +244,16 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
final float scaledImageWidth = drawableWidth * scaleToFitIconView;
final float scaledImageHeight = drawableHeight * scaleToFitIconView;
- // if the scaled image size <= mOriginalStatusBarIconSize, we don't need to enlarge it
scaleToOriginalDrawingSize = Math.min(
(float) mOriginalStatusBarIconSize / scaledImageWidth,
(float) mOriginalStatusBarIconSize / scaledImageHeight);
if (scaleToOriginalDrawingSize > 1.0f) {
- scaleToOriginalDrawingSize = 1.0f;
+ // per b/296026932, if the scaled image size <= mOriginalStatusBarIconSize, we need
+ // to scale up the scaled image to fit in mOriginalStatusBarIconSize. But if both
+ // the raw drawable intrinsic width/height are less than mOriginalStatusBarIconSize,
+ // then we just scale up the scaled image back to the raw drawable size.
+ scaleToOriginalDrawingSize = Math.min(
+ scaleToOriginalDrawingSize, 1f / scaleToFitIconView);
}
}
iconScale = scaleToOriginalDrawingSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index e632214bcb2b..37a4ef168423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -104,8 +104,9 @@ public class StatusBarStateControllerImpl implements
// Record the HISTORY_SIZE most recent states
private int mHistoryIndex = 0;
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
- // This is used by InteractionJankMonitor to get callback from HWUI.
+ // These views are used by InteractionJankMonitor to get callback from HWUI.
private View mView;
+ private KeyguardClockSwitch mClockSwitchView;
/**
* If any of the system bars is hidden.
@@ -334,6 +335,7 @@ public class StatusBarStateControllerImpl implements
if ((mView == null || !mView.isAttachedToWindow())
&& (view != null && view.isAttachedToWindow())) {
mView = view;
+ mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
}
mDozeAmountTarget = dozeAmount;
if (animated) {
@@ -416,21 +418,12 @@ public class StatusBarStateControllerImpl implements
/** Returns the id of the currently rendering clock */
public String getClockId() {
- if (mView == null) {
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
-
- View clockSwitch = mView.findViewById(R.id.keyguard_clock_container);
- if (clockSwitch == null) {
+ if (mClockSwitchView == null) {
Log.e(TAG, "Clock container was missing");
return KeyguardClockSwitch.MISSING_CLOCK_ID;
}
- if (!(clockSwitch instanceof KeyguardClockSwitch)) {
- Log.e(TAG, "Clock container was incorrect type: " + clockSwitch);
- return KeyguardClockSwitch.MISSING_CLOCK_ID;
- }
- return ((KeyguardClockSwitch) clockSwitch).getClockId();
+ return mClockSwitchView.getClockId();
}
private void beginInteractionJankMonitor() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index eb31bd3a95fe..2d5afd56da72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -50,7 +50,7 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl
* Set of summary keys whose groups are expanded.
* NOTE: This should not be modified without notifying listeners, so prefer using
* {@code setGroupExpanded} when making changes.
- */
+ */
private final Set<NotificationEntry> mExpandedGroups = new HashSet<>();
private final FeatureFlags mFeatureFlags;
@@ -104,7 +104,18 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl
@Override
public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
- final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
+ NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)
+ && entry.getParent() == null) {
+ if (expanded) {
+ throw new IllegalArgumentException("Cannot expand group that is not attached");
+ } else {
+ // The entry is no longer attached, but we still want to make sure we don't have
+ // a stale expansion state.
+ groupSummary = entry;
+ }
+ }
+
boolean changed;
if (expanded) {
changed = mExpandedGroups.add(groupSummary);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
index c33e8ab8cdd4..3158782e6fea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
@@ -25,18 +25,18 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.List;
/**
- * Helper that determines the group states (parent, summary, children) of a notification.
+ * Helper that determines the group states (parent, summary, children) of a notification. This
+ * generally assumes that the notification is attached (aka its parent is not null).
*/
public interface GroupMembershipManager {
/**
- * @return whether a given notification is a top level entry or is the summary in a group which
- * has children
+ * @return whether a given notification is the summary in a group which has children
*/
boolean isGroupSummary(@NonNull NotificationEntry entry);
/**
* Get the summary of a specified status bar notification. For an isolated notification this
- * returns itself.
+ * returns null, but if called directly on a summary it returns itself.
*/
@Nullable
NotificationEntry getGroupSummary(@NonNull NotificationEntry entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index a6b855f9b838..cb7935369564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -22,7 +22,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -38,47 +38,50 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class GroupMembershipManagerImpl implements GroupMembershipManager {
- FeatureFlags mFeatureFlags;
+ FeatureFlagsClassic mFeatureFlags;
@Inject
- public GroupMembershipManagerImpl(FeatureFlags featureFlags) {
+ public GroupMembershipManagerImpl(FeatureFlagsClassic featureFlags) {
mFeatureFlags = featureFlags;
}
@Override
public boolean isGroupSummary(@NonNull NotificationEntry entry) {
- return getGroupSummary(entry) == entry;
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
+ if (entry.getParent() == null) {
+ // The entry is not attached, so it doesn't count.
+ return false;
+ }
+ // If entry is a summary, its parent is a GroupEntry with summary = entry.
+ return entry.getParent().getSummary() == entry;
+ } else {
+ return getGroupSummary(entry) == entry;
+ }
}
@Nullable
@Override
public NotificationEntry getGroupSummary(@NonNull NotificationEntry entry) {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
- if (!isChildInGroup(entry)) {
- return entry.getRepresentativeEntry();
- }
- } else {
- if (isEntryTopLevel(entry) || entry.getParent() == null) {
- return null;
- }
+ if (isTopLevelEntry(entry) || entry.getParent() == null) {
+ return null;
}
-
- return entry.getParent().getRepresentativeEntry();
+ return entry.getParent().getSummary();
}
@Override
public boolean isChildInGroup(@NonNull NotificationEntry entry) {
if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
- return !isEntryTopLevel(entry) && entry.getParent() != null;
+ // An entry is a child if it's not a summary or top level entry, but it is attached.
+ return !isGroupSummary(entry) && !isTopLevelEntry(entry) && entry.getParent() != null;
} else {
- return !isEntryTopLevel(entry);
+ return !isTopLevelEntry(entry);
}
}
@Override
public boolean isOnlyChildInGroup(@NonNull NotificationEntry entry) {
if (entry.getParent() == null) {
- return false;
+ return false; // The entry is not attached.
}
return !isGroupSummary(entry) && entry.getParent().getChildren().size() == 1;
@@ -103,7 +106,7 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager {
return null;
}
- private boolean isEntryTopLevel(@NonNull NotificationEntry entry) {
+ private boolean isTopLevelEntry(@NonNull NotificationEntry entry) {
return entry.getParent() == ROOT_ENTRY;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 88994b9eec04..6ec9dbe003a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -100,7 +100,11 @@ public interface NotificationInterruptStateProvider {
/**
* The notification is coming from a suspended packages, so FSI is suppressed.
*/
- NO_FSI_SUSPENDED(false);
+ NO_FSI_SUSPENDED(false),
+ /**
+ * The device is not provisioned, launch FSI.
+ */
+ FSI_NOT_PROVISIONED(true);
public final boolean shouldLaunch;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 0c43da066fdb..3819843aa7b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -75,6 +76,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private final UiEventLogger mUiEventLogger;
private final UserTracker mUserTracker;
+ private final DeviceProvisionedController mDeviceProvisionedController;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
@@ -121,7 +123,8 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
mContentResolver = contentResolver;
mPowerManager = powerManager;
mBatteryController = batteryController;
@@ -163,6 +166,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
headsUpObserver);
}
headsUpObserver.onChange(true); // set up
+ mDeviceProvisionedController = deviceProvisionedController;
}
@Override
@@ -334,6 +338,12 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
}
}
+ // The device is not provisioned, launch FSI.
+ if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+ return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_NOT_PROVISIONED,
+ suppressedByDND);
+ }
+
// Detect the case determined by b/231322873 to launch FSI while device is in use,
// as blocked by the correct implementation, and report the event.
return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
new file mode 100644
index 000000000000..4deebdb8de7d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.CallLayout
+import javax.inject.Inject
+
+class CallLayoutSetDataAsyncFactory @Inject constructor() : NotifRemoteViewsFactory {
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @NotificationRowContentBinder.InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet
+ ): View? =
+ if (name == CallLayout::class.java.name)
+ CallLayout(context, attrs).apply { setSetDataAsyncEnabled(true) }
+ else null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 4114eb2a7896..a8d59d83d1e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -71,15 +71,15 @@ class ChannelEditorDialogController @Inject constructor(
private var appUid: Int? = null
private var packageName: String? = null
private var appName: String? = null
+ private var channel: NotificationChannel? = null
private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null
// Caller should set this if they care about when we dismiss
var onFinishListener: OnChannelEditorDialogFinishedListener? = null
- @VisibleForTesting
- internal val paddedChannels = mutableListOf<NotificationChannel>()
// Channels handed to us from NotificationInfo
- private val providedChannels = mutableListOf<NotificationChannel>()
+ @VisibleForTesting
+ internal val channelList = mutableListOf<NotificationChannel>()
// Map from NotificationChannel to importance
private val edits = mutableMapOf<NotificationChannel, Int>()
@@ -93,14 +93,14 @@ class ChannelEditorDialogController @Inject constructor(
private val channelGroupList = mutableListOf<NotificationChannelGroup>()
/**
- * Give the controller all of the information it needs to present the dialog
+ * Give the controller all the information it needs to present the dialog
* for a given app. Does a bunch of querying of NoMan, but won't present anything yet
*/
fun prepareDialogForApp(
appName: String,
packageName: String,
uid: Int,
- channels: Set<NotificationChannel>,
+ channel: NotificationChannel,
appIcon: Drawable,
onSettingsClickListener: NotificationInfo.OnSettingsClickListener?
) {
@@ -110,6 +110,7 @@ class ChannelEditorDialogController @Inject constructor(
this.appIcon = appIcon
this.appNotificationsEnabled = checkAreAppNotificationsOn()
this.onSettingsClickListener = onSettingsClickListener
+ this.channel = channel
// These will always start out the same
appNotificationsCurrentlyEnabled = appNotificationsEnabled
@@ -117,9 +118,7 @@ class ChannelEditorDialogController @Inject constructor(
channelGroupList.clear()
channelGroupList.addAll(fetchNotificationChannelGroups())
buildGroupNameLookup()
- providedChannels.clear()
- providedChannels.addAll(channels)
- padToFourChannels(channels)
+ populateChannelList()
initDialog()
prepared = true
@@ -133,36 +132,26 @@ class ChannelEditorDialogController @Inject constructor(
}
}
- private fun padToFourChannels(channels: Set<NotificationChannel>) {
- paddedChannels.clear()
- // First, add all of the given channels
- paddedChannels.addAll(channels.asSequence().take(4))
-
- // Then pad to 4 if we haven't been given that many
- paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence())
- .filterNot { paddedChannels.contains(it) }
- .distinct()
- .take(4 - paddedChannels.size))
-
- // If we only got one channel and it has the default miscellaneous tag, then we actually
- // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the
- // channel
- if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) {
- paddedChannels.clear()
+ private fun populateChannelList() {
+ channelList.clear()
+ if (DEFAULT_CHANNEL_ID != channel!!.id) {
+ channelList.add(0, channel!!)
+ channelList.addAll(getDisplayableChannels(channelGroupList.asSequence())
+ .filterNot { it.id == channel!!.id }
+ .distinct())
}
}
private fun getDisplayableChannels(
groupList: Sequence<NotificationChannelGroup>
): Sequence<NotificationChannel> {
-
- // TODO (b/194833441): remove channel level settings when we move to a permission
val channels = groupList
.flatMap { group ->
- group.channels.asSequence().filterNot { channel ->
- channel.importance == IMPORTANCE_NONE ||
+ group.channels.asSequence()
+ .sortedWith(compareBy {group.name?.toString() ?: group.id})
+ .filterNot { channel ->
channel.isImportanceLockedByCriticalDeviceFunction
- }
+ }
}
// TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not)
@@ -196,8 +185,7 @@ class ChannelEditorDialogController @Inject constructor(
appNotificationsCurrentlyEnabled = null
edits.clear()
- paddedChannels.clear()
- providedChannels.clear()
+ channelList.clear()
groupNameLookup.clear()
}
@@ -231,7 +219,7 @@ class ChannelEditorDialogController @Inject constructor(
@Suppress("unchecked_cast")
private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
return try {
- noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
+ noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!)
.list as? List<NotificationChannelGroup> ?: listOf()
} catch (e: Exception) {
Log.e(TAG, "Error fetching channel groups", e)
@@ -280,7 +268,6 @@ class ChannelEditorDialogController @Inject constructor(
@VisibleForTesting
fun launchSettings(sender: View) {
- val channel = if (providedChannels.size == 1) providedChannels[0] else null
onSettingsClickListener?.onClick(sender, channel, appUid!!)
}
@@ -301,14 +288,12 @@ class ChannelEditorDialogController @Inject constructor(
controller = this@ChannelEditorDialogController
appIcon = this@ChannelEditorDialogController.appIcon
appName = this@ChannelEditorDialogController.appName
- channels = paddedChannels
+ channels = channelList
}
setOnShowListener {
- // play a highlight animation for the given channels
- for (channel in providedChannels) {
- listView?.highlightChannel(channel)
- }
+ // play a highlight animation for the given channel
+ listView?.highlightChannel(channel!!)
}
findViewById<TextView>(R.id.done_button)?.setOnClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 2cfd075a17a9..10e67a40ebc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -20,6 +20,7 @@ import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.NotificationManager.IMPORTANCE_NONE
import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
import android.content.Context
@@ -55,12 +56,14 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a
// The first row is for the entire app
private lateinit var appControlRow: AppControlView
+ private lateinit var channelListView: LinearLayout
private val channelRows = mutableListOf<ChannelRow>()
override fun onFinishInflate() {
super.onFinishInflate()
appControlRow = requireViewById(R.id.app_control)
+ channelListView = requireViewById(R.id.scrollView)
}
/**
@@ -102,7 +105,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a
// Remove any rows
for (row in channelRows) {
- removeView(row)
+ channelListView.removeView(row)
}
channelRows.clear()
@@ -122,7 +125,7 @@ class ChannelEditorListView(c: Context, attrs: AttributeSet) : LinearLayout(c, a
row.channel = channel
channelRows.add(row)
- addView(row)
+ channelListView.addView(row)
}
private fun updateAppControlRow(enabled: Boolean) {
@@ -179,7 +182,9 @@ class ChannelRow(c: Context, attrs: AttributeSet) : LinearLayout(c, attrs) {
switch = requireViewById(R.id.toggle)
switch.setOnCheckedChangeListener { _, b ->
channel?.let {
- controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE)
+ controller.proposeEditForChannel(it,
+ if (b) it.originalImportance.coerceAtLeast(IMPORTANCE_LOW)
+ else IMPORTANCE_NONE)
}
}
setOnClickListener { switch.toggle() }
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 fb8024c88191..d18f9919604d 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
@@ -2658,42 +2658,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
- * Returns the number of channels covered by the notification row (including its children if
- * it's a summary notification).
- */
- public int getNumUniqueChannels() {
- return getUniqueChannels().size();
- }
-
- /**
- * Returns the channels covered by the notification row (including its children if
- * it's a summary notification).
- */
- public ArraySet<NotificationChannel> getUniqueChannels() {
- ArraySet<NotificationChannel> channels = new ArraySet<>();
-
- channels.add(mEntry.getChannel());
-
- // If this is a summary, then add in the children notification channels for the
- // same user and pkg.
- if (mIsSummaryWithChildren) {
- final List<ExpandableNotificationRow> childrenRows = getAttachedChildren();
- final int numChildren = childrenRows.size();
- for (int i = 0; i < numChildren; i++) {
- final ExpandableNotificationRow childRow = childrenRows.get(i);
- final NotificationChannel childChannel = childRow.getEntry().getChannel();
- final StatusBarNotification childSbn = childRow.getEntry().getSbn();
- if (childSbn.getUser().equals(mEntry.getSbn().getUser())
- && childSbn.getPackageName().equals(mEntry.getSbn().getPackageName())) {
- channels.add(childChannel);
- }
- }
- }
-
- return channels;
- }
-
- /**
* If this is a group, update the appearance of the children.
*/
public void updateChildrenAppearance() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 1dd3739d0e84..6d6566058aed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -403,7 +403,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mChannelEditorDialogController,
packageName,
row.getEntry().getChannel(),
- row.getUniqueChannels(),
row.getEntry(),
onSettingsClick,
onAppSettingsClick,
@@ -449,7 +448,6 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mChannelEditorDialogController,
packageName,
row.getEntry().getChannel(),
- row.getUniqueChannels(),
row.getEntry(),
onSettingsClick,
mDeviceProvisionedController.isDeviceProvisioned(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index d8f31d4fb8a2..d8ebd4209c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -104,8 +104,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private String mAppName;
private int mAppUid;
private String mDelegatePkg;
- private int mNumUniqueChannelsInRow;
- private Set<NotificationChannel> mUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
private boolean mWasShownHighPriority;
@@ -196,7 +194,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
ChannelEditorDialogController channelEditorDialogController,
String pkg,
NotificationChannel notificationChannel,
- Set<NotificationChannel> uniqueChannelsInRow,
NotificationEntry entry,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
@@ -213,8 +210,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
mPackageName = pkg;
- mUniqueChannelsInRow = uniqueChannelsInRow;
- mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
mEntry = entry;
mSbn = entry.getSbn();
mPm = pm;
@@ -236,15 +231,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
- if (mNumUniqueChannelsInRow == 0) {
- throw new IllegalArgumentException("bindNotification requires at least one channel");
- } else {
- // Special behavior for the Default channel if no other channels have been defined.
- mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
- && mSingleNotificationChannel.getId().equals(
- NotificationChannel.DEFAULT_CHANNEL_ID)
- && numTotalChannels == 1;
- }
+ mIsSingleDefaultChannel = mSingleNotificationChannel.getId().equals(
+ NotificationChannel.DEFAULT_CHANNEL_ID) && numTotalChannels == 1;
mIsAutomaticChosen = getAlertingBehavior() == BEHAVIOR_AUTOMATIC;
bindHeader();
@@ -271,11 +259,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
findViewById(R.id.turn_off_notifications).setVisibility(GONE);
- } else if (mNumUniqueChannelsInRow > 1) {
- findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
- findViewById(R.id.non_configurable_text).setVisibility(GONE);
- findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
- findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
} else {
findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
findViewById(R.id.non_configurable_text).setVisibility(GONE);
@@ -361,9 +344,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
final int appUidF = mAppUid;
return ((View view) -> {
- mOnSettingsClickListener.onClick(view,
- mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
- appUidF);
+ mOnSettingsClickListener.onClick(view, mSingleNotificationChannel, appUidF);
});
}
return null;
@@ -375,7 +356,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mPresentingChannelEditorDialog = true;
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
- mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
mGutsContainer.closeControls(this, false);
@@ -392,7 +373,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private void bindName() {
final TextView channelName = findViewById(R.id.channel_name);
- if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
+ if (mIsSingleDefaultChannel) {
channelName.setVisibility(View.GONE);
} else {
channelName.setText(mSingleNotificationChannel.getName());
@@ -459,7 +440,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
bgHandler.post(
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
- mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+ mSingleNotificationChannel,
mStartingChannelImportance, newImportance, mIsAutomaticChosen));
mOnUserInteractionCallback.onImportanceChanged(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 0239afc08ec5..3a59978d415c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -61,7 +61,8 @@ public abstract class NotificationRowModule {
static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories(
FeatureFlags featureFlags,
PrecomputedTextViewFactory precomputedTextViewFactory,
- BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory
+ BigPictureLayoutInflaterFactory bigPictureLayoutInflaterFactory,
+ CallLayoutSetDataAsyncFactory callLayoutSetDataAsyncFactory
) {
final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>();
if (featureFlags.isEnabled(Flags.PRECOMPUTED_TEXT)) {
@@ -70,6 +71,9 @@ public abstract class NotificationRowModule {
if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
replacementFactories.add(bigPictureLayoutInflaterFactory);
}
+ if (featureFlags.isEnabled(Flags.CALL_LAYOUT_ASYNC_SET_DATA)) {
+ replacementFactories.add(callLayoutSetDataAsyncFactory);
+ }
return replacementFactories;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index 06c3b7951db3..53f7d4bde70f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -57,7 +57,6 @@ public class PartialConversationInfo extends LinearLayout implements
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
private boolean mIsNonBlockable;
- private Set<NotificationChannel> mUniqueChannelsInRow;
private Drawable mPkgIcon;
private boolean mPresentingChannelEditorDialog = false;
@@ -83,7 +82,6 @@ public class PartialConversationInfo extends LinearLayout implements
ChannelEditorDialogController channelEditorDialogController,
String pkg,
NotificationChannel notificationChannel,
- Set<NotificationChannel> uniqueChannelsInRow,
NotificationEntry entry,
NotificationInfo.OnSettingsClickListener onSettingsClick,
boolean isDeviceProvisioned,
@@ -100,7 +98,6 @@ public class PartialConversationInfo extends LinearLayout implements
mIsDeviceProvisioned = isDeviceProvisioned;
mIsNonBlockable = isNonBlockable;
mChannelEditorDialogController = channelEditorDialogController;
- mUniqueChannelsInRow = uniqueChannelsInRow;
bindHeader();
bindActions();
@@ -149,7 +146,7 @@ public class PartialConversationInfo extends LinearLayout implements
mPresentingChannelEditorDialog = true;
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
- mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mNotificationChannel, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
mGutsContainer.closeControls(this, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index 875a409c07f0..91b12ccf919a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -20,6 +20,9 @@ import static android.view.View.VISIBLE;
import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.DEFAULT_HEADER_VISIBLE_AMOUNT;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -34,8 +37,7 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import androidx.annotation.Nullable;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.NotificationActionListLayout;
import com.android.systemui.Dependency;
@@ -49,6 +51,8 @@ import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import java.util.function.Consumer;
+
/**
* Wraps a notification view inflated from a template.
*/
@@ -66,9 +70,13 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
private int mContentHeight;
private int mMinHeightHint;
+ @Nullable
private NotificationActionListLayout mActions;
- private ArraySet<PendingIntent> mCancelledPendingIntents = new ArraySet<>();
- private UiOffloadThread mUiOffloadThread;
+ // Holds list of pending intents that have been cancelled by now - we only keep hash codes
+ // to avoid holding full binder proxies for intents that may have been removed by now.
+ @NonNull
+ @VisibleForTesting
+ final ArraySet<Integer> mCancelledPendingIntents = new ArraySet<>();
private View mRemoteInputHistory;
private boolean mCanHideHeader;
private float mHeaderTranslation;
@@ -147,6 +155,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
com.android.internal.R.dimen.notification_content_margin_top);
}
+ @MainThread
private void resolveTemplateViews(StatusBarNotification sbn) {
mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
if (mRightIcon != null) {
@@ -195,34 +204,57 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
return getLargeIcon(n);
}
+ @MainThread
private void updatePendingIntentCancellations() {
if (mActions != null) {
int numActions = mActions.getChildCount();
+ final ArraySet<Integer> currentlyActivePendingIntents = new ArraySet<>(numActions);
for (int i = 0; i < numActions; i++) {
Button action = (Button) mActions.getChildAt(i);
- performOnPendingIntentCancellation(action, () -> {
- if (action.isEnabled()) {
- action.setEnabled(false);
- // The visual appearance doesn't look disabled enough yet, let's add the
- // alpha as well. Since Alpha doesn't play nicely right now with the
- // transformation, we rather blend it manually with the background color.
- ColorStateList textColors = action.getTextColors();
- int[] colors = textColors.getColors();
- int[] newColors = new int[colors.length];
- float disabledAlpha = mView.getResources().getFloat(
- com.android.internal.R.dimen.notification_action_disabled_alpha);
- for (int j = 0; j < colors.length; j++) {
- int color = colors[j];
- color = blendColorWithBackground(color, disabledAlpha);
- newColors[j] = color;
- }
- ColorStateList newColorStateList = new ColorStateList(
- textColors.getStates(), newColors);
- action.setTextColor(newColorStateList);
+ PendingIntent pendingIntent = getPendingIntentForAction(action);
+ // Check if passed intent has already been cancelled in this class and immediately
+ // disable the action to avoid temporary race with enable/disable.
+ if (pendingIntent != null) {
+ int pendingIntentHashCode = getHashCodeForPendingIntent(pendingIntent);
+ currentlyActivePendingIntents.add(pendingIntentHashCode);
+ if (mCancelledPendingIntents.contains(pendingIntentHashCode)) {
+ disableActionView(action);
}
- });
+ }
+ updatePendingIntentCancellationListener(action, pendingIntent);
+ }
+
+ // This cleanup ensures that the size of this set doesn't grow into unreasonable sizes.
+ // There are scenarios where applications updated notifications with different
+ // PendingIntents which could cause this Set to grow to 1000+ elements.
+ mCancelledPendingIntents.retainAll(currentlyActivePendingIntents);
+ }
+ }
+
+ @MainThread
+ private void updatePendingIntentCancellationListener(Button action,
+ @Nullable PendingIntent pendingIntent) {
+ ActionPendingIntentCancellationHandler cancellationHandler = null;
+ if (pendingIntent != null) {
+ // Attach listeners to handle intent cancellation to this view.
+ cancellationHandler = new ActionPendingIntentCancellationHandler(pendingIntent, action,
+ this::disableActionViewWithIntent);
+ action.addOnAttachStateChangeListener(cancellationHandler);
+ // Immediately fire the event if the view is already attached to register
+ // pending intent cancellation listener.
+ if (action.isAttachedToWindow()) {
+ cancellationHandler.onViewAttachedToWindow(action);
}
}
+
+ // If the view has an old attached listener, remove it to avoid leaking intents.
+ ActionPendingIntentCancellationHandler previousHandler =
+ (ActionPendingIntentCancellationHandler) action.getTag(
+ R.id.pending_intent_listener_tag);
+ if (previousHandler != null) {
+ previousHandler.remove();
+ }
+ action.setTag(R.id.pending_intent_listener_tag, cancellationHandler);
}
private int blendColorWithBackground(int color, float alpha) {
@@ -231,42 +263,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
Color.red(color), Color.green(color), Color.blue(color)), resolveBackgroundColor());
}
- private void performOnPendingIntentCancellation(View view, Runnable cancellationRunnable) {
- PendingIntent pendingIntent = (PendingIntent) view.getTag(
- com.android.internal.R.id.pending_intent_tag);
- if (pendingIntent == null) {
- return;
- }
- if (mCancelledPendingIntents.contains(pendingIntent)) {
- cancellationRunnable.run();
- } else {
- PendingIntent.CancelListener listener = (PendingIntent intent) -> {
- mView.post(() -> {
- mCancelledPendingIntents.add(pendingIntent);
- cancellationRunnable.run();
- });
- };
- if (mUiOffloadThread == null) {
- mUiOffloadThread = Dependency.get(UiOffloadThread.class);
- }
- if (view.isAttachedToWindow()) {
- mUiOffloadThread.execute(() -> pendingIntent.registerCancelListener(listener));
- }
- view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- mUiOffloadThread.execute(() -> pendingIntent.registerCancelListener(listener));
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mUiOffloadThread.execute(
- () -> pendingIntent.unregisterCancelListener(listener));
- }
- });
- }
- }
-
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
@@ -364,4 +360,141 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
}
return extra + super.getExtraMeasureHeight();
}
+
+ /**
+ * This finds Action view with a given intent and disables it.
+ * With maximum of 3 views, this is sufficiently fast to iterate on main thread every time.
+ */
+ @MainThread
+ private void disableActionViewWithIntent(PendingIntent intent) {
+ mCancelledPendingIntents.add(getHashCodeForPendingIntent(intent));
+ if (mActions != null) {
+ int numActions = mActions.getChildCount();
+ for (int i = 0; i < numActions; i++) {
+ Button action = (Button) mActions.getChildAt(i);
+ PendingIntent pendingIntent = getPendingIntentForAction(action);
+ if (intent.equals(pendingIntent)) {
+ disableActionView(action);
+ }
+ }
+ }
+ }
+
+ /**
+ * Disables Action view when, e.g., its PendingIntent is disabled.
+ */
+ @MainThread
+ private void disableActionView(Button action) {
+ if (action.isEnabled()) {
+ action.setEnabled(false);
+ // The visual appearance doesn't look disabled enough yet, let's add the
+ // alpha as well. Since Alpha doesn't play nicely right now with the
+ // transformation, we rather blend it manually with the background color.
+ ColorStateList textColors = action.getTextColors();
+ int[] colors = textColors.getColors();
+ int[] newColors = new int[colors.length];
+ float disabledAlpha = mView.getResources().getFloat(
+ com.android.internal.R.dimen.notification_action_disabled_alpha);
+ for (int j = 0; j < colors.length; j++) {
+ int color = colors[j];
+ color = blendColorWithBackground(color, disabledAlpha);
+ newColors[j] = color;
+ }
+ ColorStateList newColorStateList = new ColorStateList(
+ textColors.getStates(), newColors);
+ action.setTextColor(newColorStateList);
+ }
+ }
+
+ /**
+ * Returns the hashcode of underlying target of PendingIntent. We can get multiple
+ * Java PendingIntent wrapper objects pointing to the same cancelled PI in system_server.
+ * This makes sure we treat them equally.
+ */
+ private static int getHashCodeForPendingIntent(PendingIntent pendingIntent) {
+ return System.identityHashCode(pendingIntent.getTarget().asBinder());
+ }
+
+ /**
+ * Returns PendingIntent contained in the action tag. May be null.
+ */
+ @Nullable
+ private static PendingIntent getPendingIntentForAction(View action) {
+ return (PendingIntent) action.getTag(com.android.internal.R.id.pending_intent_tag);
+ }
+
+ /**
+ * Registers listeners for pending intent cancellation when Action views are attached
+ * to window.
+ * It calls onCancelPendingIntentForActionView when a PendingIntent is cancelled.
+ */
+ @VisibleForTesting
+ static final class ActionPendingIntentCancellationHandler
+ implements View.OnAttachStateChangeListener {
+
+ @Nullable
+ private static UiOffloadThread sUiOffloadThread = null;
+
+ @NonNull
+ private static UiOffloadThread getUiOffloadThread() {
+ if (sUiOffloadThread == null) {
+ sUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ }
+ return sUiOffloadThread;
+ }
+
+ private final View mView;
+ private final Consumer<PendingIntent> mOnCancelledCallback;
+
+ private final PendingIntent mPendingIntent;
+
+ ActionPendingIntentCancellationHandler(PendingIntent pendingIntent, View actionView,
+ Consumer<PendingIntent> onCancelled) {
+ this.mPendingIntent = pendingIntent;
+ this.mView = actionView;
+ this.mOnCancelledCallback = onCancelled;
+ }
+
+ private final PendingIntent.CancelListener mCancelListener =
+ new PendingIntent.CancelListener() {
+ @Override
+ public void onCanceled(PendingIntent pendingIntent) {
+ mView.post(() -> {
+ mOnCancelledCallback.accept(pendingIntent);
+ // We don't need this listener anymore once the intent was cancelled.
+ remove();
+ });
+ }
+ };
+
+ @MainThread
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ // This is safe to call multiple times with the same listener instance.
+ getUiOffloadThread().execute(() -> {
+ mPendingIntent.registerCancelListener(mCancelListener);
+ });
+ }
+
+ @MainThread
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ // This is safe to call multiple times with the same listener instance.
+ getUiOffloadThread().execute(() ->
+ mPendingIntent.unregisterCancelListener(mCancelListener));
+ }
+
+ /**
+ * Removes this listener from callbacks and releases the held PendingIntent.
+ */
+ @MainThread
+ public void remove() {
+ mView.removeOnAttachStateChangeListener(this);
+ if (mView.getTag(R.id.pending_intent_listener_tag) == this) {
+ mView.setTag(R.id.pending_intent_listener_tag, null);
+ }
+ getUiOffloadThread().execute(() ->
+ mPendingIntent.unregisterCancelListener(mCancelListener));
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 485ab3262725..f7ff39c8869b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -75,6 +75,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import dagger.Lazy;
+import java.util.Arrays;
import java.util.Optional;
import javax.inject.Inject;
@@ -201,6 +202,11 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
}
@Override
+ public void setQsTiles(String[] tiles) {
+ mQSHost.changeTilesByUser(mQSHost.getSpecs(), Arrays.stream(tiles).toList());
+ }
+
+ @Override
public void clickTile(ComponentName tile) {
// Can't inject this because it changes with the QS fragment
QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
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 6f69ea8170c0..05bedede01a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -24,9 +24,11 @@ import static android.app.StatusBarManager.windowStateToString;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+
import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -121,7 +123,6 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogTags;
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
@@ -165,8 +166,9 @@ import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
@@ -244,8 +246,6 @@ import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -257,6 +257,8 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -1346,9 +1348,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
});
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
- if (qs instanceof QSFragment) {
- mQSPanelController = ((QSFragment) qs).getQSPanelController();
- ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
+ if (qs instanceof QSFragmentLegacy) {
+ QSFragmentLegacy qsFragment = (QSFragmentLegacy) qs;
+ mQSPanelController = qsFragment.getQSPanelController();
+ qsFragment.setBrightnessMirrorController(mBrightnessMirrorController);
}
});
}
@@ -1502,7 +1505,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected QS createDefaultQSFragment() {
return mFragmentService
.getFragmentHostManager(getNotificationShadeWindowView())
- .create(QSFragment.class);
+ .create(QSFragmentLegacy.class);
}
private void setUpPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index b53939e594fb..a27e67b965a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -112,6 +112,9 @@ public class PhoneStatusBarView extends FrameLayout {
mDisplayCutout = null;
}
+ // Per b/300629388, we let the PhoneStatusBarView detect onConfigurationChanged to
+ // updateResources, instead of letting the PhoneStatusBarViewController detect onConfigChanged
+ // then notify PhoneStatusBarView.
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index e1096e2d3482..f9702dd12535 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone
import android.app.StatusBarManager.WINDOW_STATUS_BAR
-import android.content.res.Configuration
import android.graphics.Point
import android.util.Log
import android.view.MotionEvent
@@ -72,10 +71,6 @@ private constructor(
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- mView.updateResources()
- }
-
override fun onDensityOrFontScaleChanged() {
mView.onDensityOrFontScaleChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 02473f276b4b..aacdc6323179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -186,6 +186,10 @@ constructor(
}
)
}
+
+ fun logOnSimStateChanged() {
+ buffer.log(TAG, LogLevel.INFO, "onSimStateChanged")
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index ea77163f0556..cf1c97c7271f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -90,4 +90,12 @@ interface MobileConnectionsRepository {
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
val defaultMobileIconGroup: Flow<MobileIconGroup>
+
+ /**
+ * If any active SIM on the device is in
+ * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED]
+ */
+ val isAnySimSecure: Flow<Boolean>
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 991ff56e683c..22916315d274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -151,6 +151,8 @@ constructor(
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
+ override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+
override val defaultDataSubId: StateFlow<Int> =
activeRepo
.flatMapLatest { it.defaultDataSubId }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index ee13d93e735d..c7987e2f8eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -134,6 +134,8 @@ constructor(
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
+ override val isAnySimSecure: Flow<Boolean> = flowOf(false)
+
override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index cd6862113ee9..dc50990d002a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -71,6 +71,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
@@ -181,6 +182,7 @@ class MobileConnectionRepositoryImpl(
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}
+ .flowOn(bgDispatcher)
.scan(initial = initial) { state, event -> state.applyEvent(event) }
.stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
}
@@ -358,6 +360,7 @@ class MobileConnectionRepositoryImpl(
awaitClose { context.unregisterReceiver(receiver) }
}
+ .flowOn(bgDispatcher)
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled = run {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 74a849a8c01f..ecb80f28de3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -28,9 +28,10 @@ import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -38,6 +39,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
@@ -94,6 +96,7 @@ constructor(
// See [CarrierMergedConnectionRepository] for details.
wifiRepository: WifiRepository,
private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : MobileConnectionsRepository {
private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
mutableMapOf()
@@ -134,22 +137,24 @@ constructor(
)
.stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
- private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
- val callback =
- object : SubscriptionManager.OnSubscriptionsChangedListener() {
- override fun onSubscriptionsChanged() {
- logger.logOnSubscriptionsChanged()
- trySend(Unit)
- }
- }
+ private val mobileSubscriptionsChangeEvent: Flow<Unit> =
+ conflatedCallbackFlow {
+ val callback =
+ object : SubscriptionManager.OnSubscriptionsChangedListener() {
+ override fun onSubscriptionsChanged() {
+ logger.logOnSubscriptionsChanged()
+ trySend(Unit)
+ }
+ }
- subscriptionManager.addOnSubscriptionsChangedListener(
- bgDispatcher.asExecutor(),
- callback,
- )
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
- awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
- }
+ awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
+ }
+ .flowOn(bgDispatcher)
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
@@ -184,6 +189,7 @@ constructor(
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}
+ .flowOn(bgDispatcher)
.distinctUntilChanged()
.logDiffsForTable(
tableLogger,
@@ -250,6 +256,27 @@ constructor(
.distinctUntilChanged()
.onEach { logger.logDefaultMobileIconGroup(it) }
+ override val isAnySimSecure: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) {
+ logger.logOnSimStateChanged()
+ trySend(keyguardUpdateMonitor.isSimPinSecure)
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySend(false)
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+ .logDiffsForTable(
+ tableLogger,
+ LOGGING_PREFIX,
+ columnName = "isAnySimSecure",
+ initialValue = false,
+ )
+ .distinctUntilChanged()
+
override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
getOrCreateRepoForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index efa092bb3f40..250fe53a2df6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -22,11 +22,12 @@ import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-import com.android.systemui.res.R
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -90,50 +91,56 @@ sealed interface WifiIcon : Diffable<WifiIcon> {
)}"
)
)
- is WifiNetworkModel.Active -> {
- val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[model.level])
- val contentDescription =
- ContentDescription.Loaded(
- if (model.isValidated) {
- (levelDesc)
- } else {
- "$levelDesc,${context.getString(NO_INTERNET)}"
- }
- )
- Visible(model.toIcon(showHotspotInfo), contentDescription)
- }
+ is WifiNetworkModel.Active -> model.toIcon(showHotspotInfo, context)
}
- @DrawableRes
- private fun WifiNetworkModel.Active.toIcon(showHotspotInfo: Boolean): Int {
- return if (!showHotspotInfo) {
- this.toBasicIcon()
+ private fun WifiNetworkModel.Active.toIcon(
+ showHotspotInfo: Boolean,
+ context: Context,
+ ): Visible {
+ return if (
+ !showHotspotInfo ||
+ this.hotspotDeviceType == WifiNetworkModel.HotspotDeviceType.NONE
+ ) {
+ this.toBasicIcon(context)
} else {
- when (this.hotspotDeviceType) {
- WifiNetworkModel.HotspotDeviceType.NONE -> this.toBasicIcon()
- WifiNetworkModel.HotspotDeviceType.TABLET ->
- com.android.settingslib.R.drawable.ic_hotspot_tablet
- WifiNetworkModel.HotspotDeviceType.LAPTOP ->
- com.android.settingslib.R.drawable.ic_hotspot_laptop
- WifiNetworkModel.HotspotDeviceType.WATCH ->
- com.android.settingslib.R.drawable.ic_hotspot_watch
- WifiNetworkModel.HotspotDeviceType.AUTO ->
- com.android.settingslib.R.drawable.ic_hotspot_auto
- // Use phone as the default drawable
- WifiNetworkModel.HotspotDeviceType.PHONE,
- WifiNetworkModel.HotspotDeviceType.UNKNOWN,
- WifiNetworkModel.HotspotDeviceType.INVALID ->
- com.android.settingslib.R.drawable.ic_hotspot_phone
- }
+ val icon =
+ when (this.hotspotDeviceType) {
+ WifiNetworkModel.HotspotDeviceType.TABLET ->
+ com.android.settingslib.R.drawable.ic_hotspot_tablet
+ WifiNetworkModel.HotspotDeviceType.LAPTOP ->
+ com.android.settingslib.R.drawable.ic_hotspot_laptop
+ WifiNetworkModel.HotspotDeviceType.WATCH ->
+ com.android.settingslib.R.drawable.ic_hotspot_watch
+ WifiNetworkModel.HotspotDeviceType.AUTO ->
+ com.android.settingslib.R.drawable.ic_hotspot_auto
+ // Use phone as the default drawable
+ WifiNetworkModel.HotspotDeviceType.PHONE,
+ WifiNetworkModel.HotspotDeviceType.UNKNOWN,
+ WifiNetworkModel.HotspotDeviceType.INVALID ->
+ com.android.settingslib.R.drawable.ic_hotspot_phone
+ WifiNetworkModel.HotspotDeviceType.NONE ->
+ throw IllegalStateException("NONE checked earlier")
+ }
+ Visible(
+ icon,
+ ContentDescription.Loaded(context.getString(WIFI_OTHER_DEVICE_CONNECTION)),
+ )
}
}
- @DrawableRes
- private fun WifiNetworkModel.Active.toBasicIcon(): Int {
+ private fun WifiNetworkModel.Active.toBasicIcon(context: Context): Visible {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
return if (this.isValidated) {
- WifiIcons.WIFI_FULL_ICONS[this.level]
+ Visible(
+ WifiIcons.WIFI_FULL_ICONS[this.level],
+ ContentDescription.Loaded(levelDesc),
+ )
} else {
- WifiIcons.WIFI_NO_INTERNET_ICONS[this.level]
+ Visible(
+ WifiIcons.WIFI_NO_INTERNET_ICONS[this.level],
+ ContentDescription.Loaded("$levelDesc,${context.getString(NO_INTERNET)}"),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 01fabcc8bc1e..3008c866d207 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -159,7 +159,8 @@ public final class DeviceStateRotationLockSettingController
// Update the rotation policy, if needed, for this new device state
if (shouldBeLocked != isLocked) {
- mRotationPolicyWrapper.setRotationLock(shouldBeLocked);
+ mRotationPolicyWrapper.setRotationLock(shouldBeLocked,
+ /* caller= */"DeviceStateRotationLockSettingController#readPersistedSetting");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 1158324567ff..607f1e562468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -24,8 +24,8 @@ public interface RotationLockController extends Listenable,
boolean isRotationLockAffordanceVisible();
boolean isRotationLocked();
boolean isCameraRotationEnabled();
- void setRotationLocked(boolean locked);
- void setRotationLockedAtAngle(boolean locked, int rotation);
+ void setRotationLocked(boolean locked, String caller);
+ void setRotationLockedAtAngle(boolean locked, int rotation, String caller);
public interface RotationLockControllerCallback {
void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 1eeb0ac8b3bb..797aa1f3a3dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -93,12 +93,12 @@ public final class RotationLockControllerImpl implements RotationLockController
return mRotationPolicy.isCameraRotationEnabled();
}
- public void setRotationLocked(boolean locked) {
- mRotationPolicy.setRotationLock(locked);
+ public void setRotationLocked(boolean locked, String caller) {
+ mRotationPolicy.setRotationLock(locked, caller);
}
- public void setRotationLockedAtAngle(boolean locked, int rotation) {
- mRotationPolicy.setRotationLockAtAngle(locked, rotation);
+ public void setRotationLockedAtAngle(boolean locked, int rotation, String caller) {
+ mRotationPolicy.setRotationLockAtAngle(locked, rotation, caller);
}
public boolean isRotationLockAffordanceVisible() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index e4894992b49f..ce9d6fd752c5 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -37,7 +37,6 @@ import com.android.internal.logging.UiEventLogger
import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.res.R
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -50,6 +49,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.CreateUserActivity
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
@@ -62,6 +62,7 @@ import com.android.systemui.user.shared.model.UserModel
import com.android.systemui.user.utils.MultiUserActionsEvent
import com.android.systemui.user.utils.MultiUserActionsEventHelper
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.utils.UserRestrictionChecker
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -103,6 +104,7 @@ constructor(
private val refreshUsersScheduler: RefreshUsersScheduler,
private val guestUserInteractor: GuestUserInteractor,
private val uiEventLogger: UiEventLogger,
+ private val userRestrictionChecker: UserRestrictionChecker,
) {
/**
* Defines interface for classes that can be notified when the state of users on the device is
@@ -593,6 +595,7 @@ constructor(
) &&
// If the user is auto-created is must not be currently resetting.
!(isGuestUserAutoCreated && isGuestUserResetting),
+ userRestrictionChecker = userRestrictionChecker,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 93573fa42c96..80139bd6ac0c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -22,10 +22,10 @@ import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.os.UserManager
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.systemui.res.R
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.utils.UserRestrictionChecker
/**
* Defines utility functions for helping with legacy data code for users.
@@ -68,6 +68,7 @@ object LegacyUserDataHelper {
actionType: UserActionModel,
isRestricted: Boolean,
isSwitchToEnabled: Boolean,
+ userRestrictionChecker: UserRestrictionChecker,
): UserRecord {
return UserRecord(
isGuest = actionType == UserActionModel.ENTER_GUEST_MODE,
@@ -79,6 +80,7 @@ object LegacyUserDataHelper {
getEnforcedAdmin(
context = context,
selectedUserId = selectedUserId,
+ userRestrictionChecker = userRestrictionChecker,
),
isManageUsers = actionType == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
)
@@ -103,9 +105,10 @@ object LegacyUserDataHelper {
private fun getEnforcedAdmin(
context: Context,
selectedUserId: Int,
+ userRestrictionChecker: UserRestrictionChecker
): EnforcedAdmin? {
val admin =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ userRestrictionChecker.checkIfRestrictionEnforced(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
@@ -113,7 +116,7 @@ object LegacyUserDataHelper {
?: return null
return if (
- !RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ !userRestrictionChecker.hasBaseUserRestriction(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index d8de07d185c6..374ebe0f28fb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -28,8 +28,8 @@ import javax.inject.Inject
* Testable wrapper interface around RotationPolicy {link com.android.internal.view.RotationPolicy}
*/
interface RotationPolicyWrapper {
- fun setRotationLock(enabled: Boolean)
- fun setRotationLockAtAngle(enabled: Boolean, rotation: Int)
+ fun setRotationLock(enabled: Boolean, caller: String)
+ fun setRotationLockAtAngle(enabled: Boolean, rotation: Int, caller: String)
fun getRotationLockOrientation(): Int
fun isRotationLockToggleVisible(): Boolean
fun isRotationLocked(): Boolean
@@ -44,14 +44,14 @@ class RotationPolicyWrapperImpl @Inject constructor(
) :
RotationPolicyWrapper {
- override fun setRotationLock(enabled: Boolean) {
+ override fun setRotationLock(enabled: Boolean, caller: String) {
traceSection("RotationPolicyWrapperImpl#setRotationLock") {
- RotationPolicy.setRotationLock(context, enabled)
+ RotationPolicy.setRotationLock(context, enabled, caller)
}
}
- override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
- RotationPolicy.setRotationLockAtAngle(context, enabled, rotation)
+ override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int, caller: String) {
+ RotationPolicy.setRotationLockAtAngle(context, enabled, rotation, caller)
}
override fun getRotationLockOrientation(): Int =
diff --git a/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
new file mode 100644
index 000000000000..3f8346b98d39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.utils
+
+import android.content.Context
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtilsInternal
+import javax.inject.Inject
+
+/** Proxy to call [RestrictedLockUtilsInternal] */
+class UserRestrictionChecker @Inject constructor() {
+ fun checkIfRestrictionEnforced(
+ context: Context,
+ userRestriction: String,
+ userId: Int
+ ): RestrictedLockUtils.EnforcedAdmin? {
+ return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ userRestriction,
+ userId
+ )
+ }
+
+ fun hasBaseUserRestriction(context: Context, userRestriction: String, userId: Int): Boolean {
+ return RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 727d649c8118..3c6d90dc08d2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,6 +135,7 @@ import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.RoundedCornerProgressDrawable;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -304,6 +305,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
private final FeatureFlags mFeatureFlags;
+ private final SecureSettings mSecureSettings;
+ private int mDialogTimeoutMillis;
public VolumeDialogImpl(
Context context,
@@ -320,7 +323,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings) {
mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
@@ -351,6 +355,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mUseBackgroundBlur =
mContext.getResources().getBoolean(R.bool.config_volumeDialogUseBackgroundBlur);
mInteractionJankMonitor = interactionJankMonitor;
+ mSecureSettings = secureSettings;
+ mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -515,6 +521,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = mDialog.findViewById(R.id.volume_dialog);
mDialogView.setAlpha(0);
+ mDialogTimeoutMillis = mSecureSettings.getInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
+ DIALOG_TIMEOUT_MILLIS);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -527,7 +535,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
.alpha(1)
.translationX(0)
.setDuration(mDialogShowAnimationDurationMs)
- .setListener(getJankListener(getDialogView(), TYPE_SHOW, DIALOG_TIMEOUT_MILLIS))
+ .setListener(getJankListener(getDialogView(), TYPE_SHOW, mDialogTimeoutMillis))
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
@@ -1514,7 +1522,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
AccessibilityManager.FLAG_CONTENT_TEXT
| AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
- return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(mDialogTimeoutMillis,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index cc9f3e14216e..624691b19704 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.volume.CsdWarningDialog;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -63,7 +64,8 @@ public interface VolumeModule {
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SecureSettings secureSettings) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -79,7 +81,8 @@ public interface VolumeModule {
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags);
+ featureFlags,
+ secureSettings);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index ba3dbf0a0cb7..484b1194eb8b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -20,8 +20,10 @@ import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.LayoutTransition;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.logging.KeyguardLogger;
@@ -44,6 +46,7 @@ import org.mockito.MockitoAnnotations;
public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardStatusView mKeyguardStatusView;
+
@Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
@Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock protected KeyguardStateController mKeyguardStateController;
@@ -61,6 +64,10 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
protected KeyguardStatusViewController mController;
+ @Mock protected KeyguardClockSwitch mKeyguardClockSwitch;
+ @Mock protected FrameLayout mMediaHostContainer;
+ @Mock protected LayoutTransition mMediaLayoutTransition;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -93,6 +100,8 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
};
when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+
+ when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch);
}
protected void givenViewAttached() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index b8b0198f94df..e4e2b0a8d89f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -18,14 +18,18 @@ package com.android.keyguard;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.LayoutTransition;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.View;
+import com.android.systemui.res.R;
import com.android.systemui.plugins.ClockConfig;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -124,4 +128,112 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll
mController.onDestroy();
verify(mDumpManager, times(1)).unregisterDumpable(eq(mController.getInstanceName()));
}
+
+ @Test
+ public void onInit_addsOnLayoutChangeListenerToClockSwitch() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture());
+ }
+
+ @Test
+ public void onInit_addsOnLayoutChangeListenerToMediaHostContainer() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture());
+ }
+
+ @Test
+ public void clockSwitchHeightChanged_mediaChangingLayoutTransitionEnabled() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture());
+
+ // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`.
+ // Below here is the actual test.
+
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ mController.setSplitShadeEnabled(true);
+ when(mKeyguardClockSwitch.getSplitShadeCentered()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
+ when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE);
+ when(mMediaHostContainer.getHeight()).thenReturn(200);
+ when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
+
+ when(mKeyguardClockSwitch.getHeight()).thenReturn(0);
+ listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */
+ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
+ 0, /* oldBottom = */ 200);
+ verify(mMediaLayoutTransition).enableTransitionType(LayoutTransition.CHANGING);
+ }
+
+ @Test
+ public void clockSwitchHeightNotChanged_mediaChangingLayoutTransitionNotEnabled() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardClockSwitch).addOnLayoutChangeListener(captor.capture());
+
+ // Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`.
+ // Below here is the actual test.
+
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ mController.setSplitShadeEnabled(true);
+ when(mKeyguardClockSwitch.getSplitShadeCentered()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
+ when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE);
+ when(mMediaHostContainer.getHeight()).thenReturn(200);
+ when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
+
+ when(mKeyguardClockSwitch.getHeight()).thenReturn(200);
+ listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */
+ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
+ 0, /* oldBottom = */ 200);
+ verify(mMediaLayoutTransition, never()).enableTransitionType(LayoutTransition.CHANGING);
+ }
+
+ @Test
+ public void onMediaHostContainerLayout_disablesChangingLayoutTransition() {
+ when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
+ mMediaHostContainer);
+
+ mController.onInit();
+
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture());
+
+ // Above here is the same as `onInit_addsOnLayoutChangeListenerToMediaHostContainer`.
+ // Below here is the actual test.
+
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
+
+ when(mMediaLayoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).thenReturn(
+ true);
+ listener.onLayoutChange(mMediaHostContainer, 1, 2, 3, 4, 1, 2, 3, 4);
+ verify(mMediaLayoutTransition).disableTransitionType(LayoutTransition.CHANGING);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index 86439e557f8b..58d372c68c55 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -1,5 +1,6 @@
package com.android.keyguard
+import android.animation.LayoutTransition
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -35,6 +36,20 @@ class KeyguardStatusViewTest : SysuiTestCase() {
}
@Test
+ fun mediaViewHasLayoutTransitionInDisabledState() {
+ val layoutTransition = (mediaView as ViewGroup).layoutTransition
+ assertThat(layoutTransition).isNotNull()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_APPEARING))
+ .isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_DISAPPEARING))
+ .isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.APPEARING)).isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.DISAPPEARING))
+ .isFalse()
+ assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).isFalse()
+ }
+
+ @Test
fun setChildrenTranslationYExcludingMediaView_mediaViewIsNotTranslated() {
val translationY = 1234f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 8fc63b246c9d..73654609ad7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -44,6 +44,7 @@ import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -126,6 +127,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
shadeInteractor =
ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index c0257df08949..92c8a395a696 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -395,7 +395,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val bouncerSceneKey = currentScene?.key
assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
- underTest.hide("")
+ underTest.onImeHidden()
assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
}
@@ -409,7 +409,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val notBouncerSceneKey = currentScene?.key
assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
- underTest.hide("")
+ underTest.onImeHidden()
assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
}
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 360fa5652e20..944b059450c9 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
@@ -32,7 +32,6 @@ import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -48,6 +47,10 @@ import com.android.systemui.keyguard.data.repository.BiometricType.REAR_FINGERPR
import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.eq
@@ -87,6 +90,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var biometricManager: BiometricManager
+ @Mock private lateinit var tableLogger: TableLogBuffer
@Captor
private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -97,6 +101,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
private lateinit var devicePostureRepository: FakeDevicePostureRepository
private lateinit var facePropertyRepository: FakeFacePropertyRepository
private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -112,6 +117,8 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
devicePostureRepository = FakeDevicePostureRepository()
facePropertyRepository = FakeFacePropertyRepository()
fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
}
private suspend fun createBiometricSettingsRepository() {
@@ -132,6 +139,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
dumpManager = dumpManager,
facePropertyRepository = facePropertyRepository,
fingerprintPropertyRepository = fingerprintPropertyRepository,
+ mobileConnectionsRepository = mobileConnectionsRepository,
)
testScope.runCurrent()
fingerprintPropertyRepository.setProperties(
@@ -421,6 +429,50 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
}
@Test
+ fun anySimSecure_disablesFaceAuth() =
+ testScope.runTest {
+ faceAuthIsEnrolled()
+ createBiometricSettingsRepository()
+
+ faceAuthIsEnabledByBiometricManager()
+ doNotDisableKeyguardAuthFeatures()
+ mobileConnectionsRepository.isAnySimSecure.value = false
+ runCurrent()
+
+ val isFaceAuthEnabledAndEnrolled by
+ collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
+ assertThat(isFaceAuthEnabledAndEnrolled).isTrue()
+
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ runCurrent()
+
+ assertThat(isFaceAuthEnabledAndEnrolled).isFalse()
+ }
+
+ @Test
+ fun anySimSecure_disablesFaceAuthToNotCurrentlyRun() =
+ testScope.runTest {
+ faceAuthIsEnrolled()
+
+ createBiometricSettingsRepository()
+ val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
+
+ deviceIsInPostureThatSupportsFaceAuth()
+ doNotDisableKeyguardAuthFeatures()
+ faceAuthIsStrongBiometric()
+ faceAuthIsEnabledByBiometricManager()
+ mobileConnectionsRepository.isAnySimSecure.value = false
+
+ onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
+ onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+ assertThat(isFaceAuthCurrentlyAllowed).isTrue()
+
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ assertThat(isFaceAuthCurrentlyAllowed).isFalse()
+ }
+
+ @Test
fun biometricManagerControlsFaceAuthenticationEnabledStatus() =
testScope.runTest {
faceAuthIsEnrolled()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
index 93f316e9a619..9e5d3bdb179c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
@@ -29,15 +29,16 @@ import org.junit.Test
import org.mockito.Mockito.mock
@SmallTest
-class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() {
-
- private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
- .create("buffer", 10)
- private val disableFlagsLogger = DisableFlagsLogger(
- listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
- listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
- )
- private val logger = QSFragmentDisableFlagsLogger(buffer, disableFlagsLogger)
+class QSDisableFlagsLoggerTest : SysuiTestCase() {
+
+ private val buffer =
+ LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java)).create("buffer", 10)
+ private val disableFlagsLogger =
+ DisableFlagsLogger(
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
+ )
+ private val logger = QSDisableFlagsLogger(buffer, disableFlagsLogger)
@Test
fun logDisableFlagChange_bufferHasStates() {
@@ -48,9 +49,8 @@ class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() {
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
val actualString = stringWriter.toString()
- val expectedLogString = disableFlagsLogger.getDisableFlagsString(
- new = state, newAfterLocalModification = state
- )
+ val expectedLogString =
+ disableFlagsLogger.getDisableFlagsString(new = state, newAfterLocalModification = state)
assertThat(actualString).contains(expectedLogString)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index c4c233ca5eef..d57765c3ecf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -1,15 +1,17 @@
/*
- * Copyright (C) 2016 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. You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.android.systemui.qs;
@@ -34,7 +36,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
-import android.app.Fragment;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
@@ -49,12 +50,12 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.res.R;
-import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
@@ -66,8 +67,8 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
@@ -79,10 +80,9 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
-public class QSFragmentTest extends SysuiBaseFragmentTest {
+public class QSImplTest extends SysuiTestCase {
- @Mock private QSFragmentComponent.Factory mQsComponentFactory;
- @Mock private QSFragmentComponent mQsFragmentComponent;
+ @Mock private QSComponent mQsComponent;
@Mock private QSPanelController mQSPanelController;
@Mock private MediaHost mQSMediaHost;
@Mock private MediaHost mQQSMediaHost;
@@ -107,69 +107,54 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
@Mock private FooterActionsViewBinder mFooterActionsViewBinder;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- @Mock private FeatureFlags mFeatureFlags;
- private View mQsFragmentView;
+ @Mock private FeatureFlagsClassic mFeatureFlags;
+ private View mQsView;
+
+ private QSImpl mUnderTest;
- public QSFragmentTest() {
- super(QSFragment.class);
- }
@Before
public void setup() {
- injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- }
-
- @Test
- public void testListening() {
- QSFragment qs = (QSFragment) mFragment;
- mFragments.dispatchResume();
- processAllMessages();
-
- qs.setListening(true);
- processAllMessages();
+ mUnderTest = instantiate();
- qs.setListening(false);
- processAllMessages();
+ mUnderTest.onComponentCreated(mQsComponent, null);
}
+
@Test
public void testSaveState() {
- mFragments.dispatchResume();
- processAllMessages();
+ mUnderTest.setListening(true);
+ mUnderTest.setExpanded(true);
+ mUnderTest.setQsVisible(true);
+
+ Bundle bundle = new Bundle();
+ mUnderTest.onSaveInstanceState(bundle);
- QSFragment qs = (QSFragment) mFragment;
- qs.setListening(true);
- qs.setExpanded(true);
- qs.setQsVisible(true);
- processAllMessages();
- recreateFragment();
- processAllMessages();
+ // Get a new instance
+ QSImpl other = instantiate();
+ other.onComponentCreated(mQsComponent, bundle);
- // Get the reference to the new fragment.
- qs = (QSFragment) mFragment;
- assertTrue(qs.isListening());
- assertTrue(qs.isExpanded());
- assertTrue(qs.isQsVisible());
+ assertTrue(other.isListening());
+ assertTrue(other.isExpanded());
+ assertTrue(other.isQsVisible());
}
@Test
public void transitionToFullShade_smallScreen_alphaAlways1() {
- QSFragment fragment = resumeAndGetFragment();
setIsSmallScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1f);
+ assertThat(mQsView.getAlpha()).isEqualTo(1f);
}
@Test
public void transitionToFullShade_largeScreen_alphaLargeScreenShadeInterpolator() {
- QSFragment fragment = resumeAndGetFragment();
setIsLargeScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
@@ -177,43 +162,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
float squishinessFraction = 0.5f;
when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f);
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha())
- .isEqualTo(123f);
+ assertThat(mQsView.getAlpha()).isEqualTo(123f);
}
@Test
public void
transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator() {
- QSFragment fragment = resumeAndGetFragment();
setStatusBarCurrentAndUpcomingState(KEYGUARD);
when(mQSPanelController.isBouncerInTransit()).thenReturn(false);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress);
+ assertThat(mQsView.getAlpha()).isEqualTo(transitionProgress);
}
@Test
public void
transitionToFullShade_onKeyguard_bouncerActive_setsAlphaUsingBouncerInterpolator() {
- QSFragment fragment = resumeAndGetFragment();
setStatusBarCurrentAndUpcomingState(KEYGUARD);
when(mQSPanelController.isBouncerInTransit()).thenReturn(true);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha())
+ assertThat(mQsView.getAlpha())
.isEqualTo(
BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
transitionProgress));
@@ -221,40 +203,37 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void transitionToFullShade_inFullWidth_alwaysSetsAlphaTo1() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setIsNotificationPanelFullWidth(true);
+ mUnderTest.setIsNotificationPanelFullWidth(true);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.1f;
float squishinessFraction = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
transitionProgress = 0.5f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
transitionProgress = 0.7f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+ assertThat(mQsView.getAlpha()).isEqualTo(1);
}
@Test
public void transitionToFullShade_setsSquishinessOnController() {
- QSFragment fragment = resumeAndGetFragment();
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.123f;
float squishinessFraction = 0.456f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
- verify(mQsFragmentComponent.getQSSquishinessController())
- .setSquishiness(squishinessFraction);
+ verify(mQsComponent.getQSSquishinessController()).setSquishiness(squishinessFraction);
}
@Test
@@ -265,10 +244,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
float proposedTranslation = 456f;
float squishinessFraction = 0.987f;
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
squishinessFraction);
verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
@@ -283,10 +261,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
float proposedTranslation = 456f;
float squishinessFraction = 0.987f;
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
- fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
squishinessFraction);
verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
@@ -295,7 +272,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void setQsExpansion_inSplitShade_whenTransitioningToKeyguard_setsAlphaBasedOnShadeTransitionProgress() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
@@ -303,24 +279,23 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
float transitionProgress = 0;
float squishinessFraction = 0f;
- fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+ mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
// trigger alpha refresh with non-zero expansion and fraction values
- fragment.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1,
+ mUnderTest.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1,
/* proposedTranslation= */ 0, /* squishinessFraction= */ 1);
// alpha should follow lockscreen to shade progress, not panel expansion fraction
- assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress);
+ assertThat(mQsView.getAlpha()).isEqualTo(transitionProgress);
}
@Test
public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
when(mHeader.getHeight()).thenReturn(1234);
- int height = fragment.getQsMinExpansionHeight();
+ int height = mUnderTest.getQsMinExpansionHeight();
assertThat(height).isEqualTo(mHeader.getHeight());
}
@@ -329,13 +304,12 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
public void getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomOfQSContainer() {
int top = 1234;
int height = 9876;
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- setLocationOnScreen(mQsFragmentView, top);
- when(mQsFragmentView.getHeight()).thenReturn(height);
+ setLocationOnScreen(mQsView, top);
+ when(mQsView.getHeight()).thenReturn(height);
int expectedHeight = top + height;
- assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
+ assertThat(mUnderTest.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
}
@Test
@@ -343,47 +317,43 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
int top = 1234;
int height = 9876;
float translationY = -600f;
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- setLocationOnScreen(mQsFragmentView, (int) (top + translationY));
- when(mQsFragmentView.getHeight()).thenReturn(height);
- when(mQsFragmentView.getTranslationY()).thenReturn(translationY);
+ setLocationOnScreen(mQsView, (int) (top + translationY));
+ when(mQsView.getHeight()).thenReturn(height);
+ when(mQsView.getTranslationY()).thenReturn(translationY);
int expectedHeight = top + height;
- assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
+ assertThat(mUnderTest.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
}
@Test
public void hideImmediately_notInSplitShade_movesViewUpByHeaderHeight() {
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
when(mHeader.getHeight()).thenReturn(555);
- fragment.hideImmediately();
+ mUnderTest.hideImmediately();
- assertThat(mQsFragmentView.getY()).isEqualTo(-mHeader.getHeight());
+ assertThat(mQsView.getY()).isEqualTo(-mHeader.getHeight());
}
@Test
public void hideImmediately_inSplitShade_movesViewUpByQSAbsoluteBottom() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
int top = 1234;
int height = 9876;
- setLocationOnScreen(mQsFragmentView, top);
- when(mQsFragmentView.getHeight()).thenReturn(height);
+ setLocationOnScreen(mQsView, top);
+ when(mQsView.getHeight()).thenReturn(height);
- fragment.hideImmediately();
+ mUnderTest.hideImmediately();
int qsAbsoluteBottom = top + height;
- assertThat(mQsFragmentView.getY()).isEqualTo(-qsAbsoluteBottom);
+ assertThat(mQsView.getY()).isEqualTo(-qsAbsoluteBottom);
}
@Test
public void setCollapseExpandAction_passedToControllers() {
Runnable action = () -> {};
- QSFragment fragment = resumeAndGetFragment();
- fragment.setCollapseExpandAction(action);
+ mUnderTest.setCollapseExpandAction(action);
verify(mQSPanelController).setCollapseExpandAction(action);
verify(mQuickQSPanelController).setCollapseExpandAction(action);
@@ -391,24 +361,21 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void setOverScrollAmount_setsTranslationOnView() {
- QSFragment fragment = resumeAndGetFragment();
+ mUnderTest.setOverScrollAmount(123);
- fragment.setOverScrollAmount(123);
-
- assertThat(mQsFragmentView.getTranslationY()).isEqualTo(123);
+ assertThat(mQsView.getTranslationY()).isEqualTo(123);
}
@Test
public void setOverScrollAmount_beforeViewCreated_translationIsNotSet() {
- QSFragment fragment = getFragment();
-
- fragment.setOverScrollAmount(123);
+ QSImpl other = instantiate();
+ other.setOverScrollAmount(123);
- assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0);
+ assertThat(mQsView.getTranslationY()).isEqualTo(0);
}
private Lifecycle.State getListeningAndVisibilityLifecycleState() {
- return getFragment()
+ return mUnderTest
.getListeningAndVisibilityLifecycleOwner()
.getLifecycle()
.getCurrentState();
@@ -416,11 +383,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void setListeningFalse_notVisible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(false);
+ mUnderTest.setQsVisible(false);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(false);
+ mUnderTest.setListening(false);
verify(mQSContainerImplController).setListening(false);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -428,11 +394,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void setListeningTrue_notVisible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(false);
+ mUnderTest.setQsVisible(false);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(true);
+ mUnderTest.setListening(true);
verify(mQSContainerImplController).setListening(false);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.STARTED);
verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -440,11 +405,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void setListeningFalse_visible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(true);
+ mUnderTest.setQsVisible(true);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(false);
+ mUnderTest.setListening(false);
verify(mQSContainerImplController).setListening(false);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -452,11 +416,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void setListeningTrue_visible() {
- QSFragment fragment = resumeAndGetFragment();
- fragment.setQsVisible(true);
+ mUnderTest.setQsVisible(true);
clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
- fragment.setListening(true);
+ mUnderTest.setListening(true);
verify(mQSContainerImplController).setListening(true);
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.RESUMED);
verify(mQSPanelController).setListening(eq(true), anyBoolean());
@@ -464,31 +427,28 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void passCorrectExpansionState_inSplitShade() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
clearInvocations(mQSPanelController);
- fragment.setExpanded(true);
+ mUnderTest.setExpanded(true);
verify(mQSPanelController).setExpanded(true);
- fragment.setExpanded(false);
+ mUnderTest.setExpanded(false);
verify(mQSPanelController).setExpanded(false);
}
@Test
public void startsListeningAfterStateChangeToExpanded_inSplitShade() {
- QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
- fragment.setQsVisible(true);
+ mUnderTest.setQsVisible(true);
clearInvocations(mQSPanelController);
- fragment.setExpanded(true);
+ mUnderTest.setExpanded(true);
verify(mQSPanelController).setListening(true, true);
}
@Test
public void testUpdateQSBounds_setMediaClipCorrectly() {
- QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
Rect mediaHostClip = new Rect();
@@ -497,7 +457,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
when(mQSPanelScrollView.getMeasuredHeight()).thenReturn(200);
when(mQSMediaHost.getCurrentClipping()).thenReturn(mediaHostClip);
- fragment.updateQsBounds();
+ mUnderTest.updateQsBounds();
assertEquals(25, mediaHostClip.top);
assertEquals(175, mediaHostClip.bottom);
@@ -505,17 +465,15 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Test
public void testQsUpdatesQsAnimatorWithUpcomingState() {
- QSFragment fragment = resumeAndGetFragment();
setStatusBarCurrentAndUpcomingState(SHADE);
- fragment.onUpcomingStateChanged(KEYGUARD);
+ mUnderTest.onUpcomingStateChanged(KEYGUARD);
verify(mQSAnimator).setOnKeyguard(true);
}
- @Override
- protected Fragment instantiate(Context context, String className, Bundle arguments) {
+ private QSImpl instantiate() {
MockitoAnnotations.initMocks(this);
- CommandQueue commandQueue = new CommandQueue(context, new FakeDisplayTracker(context));
+ CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
setupQsComponent();
setUpViews();
@@ -523,9 +481,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
setUpMedia();
setUpOther();
- return new QSFragment(
+ return new QSImpl(
new RemoteInputQuickSettingsDisabler(
- context,
+ mContext,
commandQueue,
new ResourcesSplitShadeStateController(),
mock(ConfigurationController.class)),
@@ -534,8 +492,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mQSMediaHost,
mQQSMediaHost,
mBypassController,
- mQsComponentFactory,
- mock(QSFragmentDisableFlagsLogger.class),
+ mock(QSDisableFlagsLogger.class),
mock(DumpManager.class),
mock(QSLogger.class),
mock(FooterActionsController.class),
@@ -561,12 +518,13 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
private void setUpViews() {
- mQsFragmentView = spy(new View(mContext));
- when(mQsFragmentView.findViewById(R.id.expanded_qs_scroll_view))
+ mQsView = spy(new View(mContext));
+ when(mQsComponent.getRootView()).thenReturn(mQsView);
+ when(mQsView.findViewById(R.id.expanded_qs_scroll_view))
.thenReturn(mQSPanelScrollView);
- when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader);
- when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
- when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer(
+ when(mQsView.findViewById(R.id.header)).thenReturn(mHeader);
+ when(mQsView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
+ when(mQsView.findViewById(R.id.qs_footer_actions)).thenAnswer(
invocation -> new FooterActionsViewBinder().create(mContext));
}
@@ -597,37 +555,26 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
return realInflater.inflate(layoutRes, root, attachToRoot);
}
- return mQsFragmentView;
+ return mQsView;
}
private void setupQsComponent() {
- when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
- when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
- when(mQsFragmentComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
- when(mQsFragmentComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController);
- when(mQsFragmentComponent.getQSContainerImplController())
+ when(mQsComponent.getQSPanelController()).thenReturn(mQSPanelController);
+ when(mQsComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
+ when(mQsComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController);
+ when(mQsComponent.getQSContainerImplController())
.thenReturn(mQSContainerImplController);
- when(mQsFragmentComponent.getQSFooter()).thenReturn(mFooter);
- when(mQsFragmentComponent.getQSFooterActionController())
+ when(mQsComponent.getQSFooter()).thenReturn(mFooter);
+ when(mQsComponent.getQSFooterActionController())
.thenReturn(mQSFooterActionController);
- when(mQsFragmentComponent.getQSAnimator()).thenReturn(mQSAnimator);
- when(mQsFragmentComponent.getQSSquishinessController()).thenReturn(mSquishinessController);
- }
-
- private QSFragment getFragment() {
- return ((QSFragment) mFragment);
- }
-
- private QSFragment resumeAndGetFragment() {
- mFragments.dispatchResume();
- processAllMessages();
- return getFragment();
+ when(mQsComponent.getQSAnimator()).thenReturn(mQSAnimator);
+ when(mQsComponent.getQSSquishinessController()).thenReturn(mSquishinessController);
}
private void setStatusBarCurrentAndUpcomingState(int statusBarState) {
when(mStatusBarStateController.getState()).thenReturn(statusBarState);
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(statusBarState);
- getFragment().onStateChanged(statusBarState);
+ mUnderTest.onStateChanged(statusBarState);
}
private void enableSplitShade() {
@@ -639,7 +586,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
private void setSplitShadeEnabled(boolean enabled) {
- getFragment().setInSplitShade(enabled);
+ mUnderTest.setInSplitShade(enabled);
}
private void setLocationOnScreen(View view, int top) {
@@ -652,10 +599,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
private void setIsLargeScreen() {
- getFragment().setIsNotificationPanelFullWidth(false);
+ mUnderTest.setIsNotificationPanelFullWidth(false);
}
private void setIsSmallScreen() {
- getFragment().setIsNotificationPanelFullWidth(true);
+ mUnderTest.setIsNotificationPanelFullWidth(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index b595e8de3bad..79411f427f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -66,6 +66,7 @@ import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.di.NewQSTileFactory;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -77,6 +78,8 @@ import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import dagger.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -102,8 +105,6 @@ public class QSTileHostTest extends SysuiTestCase {
private static final String SETTING = QSHost.TILES_SETTING;
@Mock
- private QSFactory mDefaultFactory;
- @Mock
private PluginManager mPluginManager;
@Mock
private TunerService mTunerService;
@@ -117,7 +118,6 @@ public class QSTileHostTest extends SysuiTestCase {
private CustomTile mCustomTile;
@Mock
private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
@Mock
private CustomTileStatePersister mCustomTileStatePersister;
@Mock
@@ -127,6 +127,10 @@ public class QSTileHostTest extends SysuiTestCase {
@Mock
private UserFileManager mUserFileManager;
+ private SecureSettings mSecureSettings;
+
+ private QSFactory mDefaultFactory;
+
private SparseArray<SharedPreferences> mSharedPreferencesByUser;
private FakeFeatureFlags mFeatureFlags;
@@ -144,6 +148,8 @@ public class QSTileHostTest extends SysuiTestCase {
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
+ // TODO(b/299909337): Add test checking the new factory is used when the flag is on
+ mFeatureFlags.set(Flags.QS_PIPELINE_NEW_TILES, false);
mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags);
mMainExecutor = new FakeExecutor(new FakeSystemClock());
@@ -164,7 +170,8 @@ public class QSTileHostTest extends SysuiTestCase {
mSecureSettings = new FakeSettings();
saveSetting("");
- mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
+ setUpTileFactory();
+ mQSTileHost = new TestQSTileHost(mContext, () -> null, mDefaultFactory, mMainExecutor,
mPluginManager, mTunerService, () -> mAutoTiles, mShadeController,
mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
@@ -178,7 +185,6 @@ public class QSTileHostTest extends SysuiTestCase {
mMainExecutor.runAllReady();
}
}, mUserTracker.getUserId());
- setUpTileFactory();
}
private void saveSetting(String value) {
@@ -191,32 +197,29 @@ public class QSTileHostTest extends SysuiTestCase {
}
private void setUpTileFactory() {
- // Only create this kind of tiles
- when(mDefaultFactory.createTile(anyString())).thenAnswer(
- invocation -> {
- String spec = invocation.getArgument(0);
- if ("spec1".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else if ("spec2".equals(spec)) {
- return new TestTile2(mQSTileHost);
- } else if ("spec3".equals(spec)) {
- return new TestTile3(mQSTileHost);
- } else if ("na".equals(spec)) {
- return new NotAvailableTile(mQSTileHost);
- } else if (CUSTOM_TILE_SPEC.equals(spec)) {
- QSTile tile = mCustomTile;
- QSTile.State s = mock(QSTile.State.class);
- s.spec = spec;
- when(mCustomTile.getState()).thenReturn(s);
- return tile;
- } else if ("internet".equals(spec)
- || "wifi".equals(spec)
- || "cell".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else {
- return null;
- }
- });
+ mDefaultFactory = new FakeQSFactory(spec -> {
+ if ("spec1".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else if ("spec2".equals(spec)) {
+ return new TestTile2(mQSTileHost);
+ } else if ("spec3".equals(spec)) {
+ return new TestTile3(mQSTileHost);
+ } else if ("na".equals(spec)) {
+ return new NotAvailableTile(mQSTileHost);
+ } else if (CUSTOM_TILE_SPEC.equals(spec)) {
+ QSTile tile = mCustomTile;
+ QSTile.State s = mock(QSTile.State.class);
+ s.spec = spec;
+ when(mCustomTile.getState()).thenReturn(s);
+ return tile;
+ } else if ("internet".equals(spec)
+ || "wifi".equals(spec)
+ || "cell".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else {
+ return null;
+ }
+ });
when(mCustomTile.isAvailable()).thenReturn(true);
}
@@ -703,7 +706,7 @@ public class QSTileHostTest extends SysuiTestCase {
}
private class TestQSTileHost extends QSTileHost {
- TestQSTileHost(Context context,
+ TestQSTileHost(Context context, Lazy<NewQSTileFactory> newQSTileFactoryProvider,
QSFactory defaultFactory, Executor mainExecutor,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles,
@@ -712,7 +715,7 @@ public class QSTileHostTest extends SysuiTestCase {
CustomTileStatePersister customTileStatePersister,
TileLifecycleManager.Factory tileLifecycleManagerFactory,
UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) {
- super(context, defaultFactory, mainExecutor, pluginManager,
+ super(context, newQSTileFactoryProvider, defaultFactory, mainExecutor, pluginManager,
tunerService, autoTiles, shadeController, qsLogger,
userTracker, secureSettings, customTileStatePersister,
tileLifecycleManagerFactory, userFileManager, featureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index bde30382ba05..d3cd26bcd3cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -124,6 +124,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
if (FACTORY_TILES.contains(spec)) {
FakeQSTile tile = new FakeQSTile(mBgExecutor, mMainExecutor);
tile.setState(mState);
+ tile.setTileSpec(spec);
return tile;
} else {
return null;
@@ -284,7 +285,10 @@ public class TileQueryHelperTest extends SysuiTestCase {
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
QSTile t = mock(QSTile.class);
- when(mQSHost.createTile("hotspot")).thenReturn(t);
+ when(mQSHost.createTile("hotspot")).thenAnswer(invocation -> {
+ t.setTileSpec("hotspot");
+ return t;
+ });
mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
"hotspot");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index 9386d711374a..9a55f722c13c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
@@ -23,15 +23,19 @@ import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -39,6 +43,20 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AutoAddSettingsRepositoryTest : SysuiTestCase() {
private val secureSettings = FakeSettings()
+ private val userAutoAddRepositoryFactory =
+ object : UserAutoAddRepository.Factory {
+ override fun create(userId: Int): UserAutoAddRepository {
+ return UserAutoAddRepository(
+ userId,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+ }
+
+ @Mock private lateinit var logger: QSPipelineLogger
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -47,110 +65,37 @@ class AutoAddSettingsRepositoryTest : SysuiTestCase() {
@Before
fun setUp() {
- underTest =
- AutoAddSettingRepository(
- secureSettings,
- testDispatcher,
- )
- }
-
- @Test
- fun nonExistentSetting_emptySet() =
- testScope.runTest {
- val specs by collectLastValue(underTest.autoAddedTiles(0))
-
- assertThat(specs).isEmpty()
- }
-
- @Test
- fun settingsChange_correctValues() =
- testScope.runTest {
- val userId = 0
- val specs by collectLastValue(underTest.autoAddedTiles(userId))
-
- val value = "a,custom(b/c)"
- storeForUser(value, userId)
-
- assertThat(specs).isEqualTo(value.toSet())
+ MockitoAnnotations.initMocks(this)
- val newValue = "a"
- storeForUser(newValue, userId)
-
- assertThat(specs).isEqualTo(newValue.toSet())
- }
+ underTest = AutoAddSettingRepository(userAutoAddRepositoryFactory)
+ }
@Test
fun tilesForCorrectUsers() =
testScope.runTest {
- val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
- val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
-
val user0Tiles = "a"
val user1Tiles = "custom(b/c)"
storeForUser(user0Tiles, 0)
storeForUser(user1Tiles, 1)
+ val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+ val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+ runCurrent()
- assertThat(tilesFromUser0).isEqualTo(user0Tiles.toSet())
- assertThat(tilesFromUser1).isEqualTo(user1Tiles.toSet())
- }
-
- @Test
- fun noInvalidTileSpecs() =
- testScope.runTest {
- val userId = 0
- val tiles by collectLastValue(underTest.autoAddedTiles(userId))
-
- val specs = "d,custom(bad)"
- storeForUser(specs, userId)
-
- assertThat(tiles).isEqualTo("d".toSet())
- }
-
- @Test
- fun markAdded() =
- testScope.runTest {
- val userId = 0
- val specs = mutableSetOf(TileSpec.create("a"))
- underTest.markTileAdded(userId, TileSpec.create("a"))
-
- assertThat(loadForUser(userId).toSet()).containsExactlyElementsIn(specs)
-
- specs.add(TileSpec.create("b"))
- underTest.markTileAdded(userId, TileSpec.create("b"))
-
- assertThat(loadForUser(userId).toSet()).containsExactlyElementsIn(specs)
+ assertThat(tilesFromUser0).isEqualTo(user0Tiles.toTilesSet())
+ assertThat(tilesFromUser1).isEqualTo(user1Tiles.toTilesSet())
}
@Test
fun markAdded_multipleUsers() =
testScope.runTest {
- underTest.markTileAdded(userId = 1, TileSpec.create("a"))
-
- assertThat(loadForUser(0).toSet()).isEmpty()
- assertThat(loadForUser(1).toSet())
- .containsExactlyElementsIn(setOf(TileSpec.create("a")))
- }
-
- @Test
- fun markAdded_Invalid_noop() =
- testScope.runTest {
- val userId = 0
- underTest.markTileAdded(userId, TileSpec.Invalid)
-
- assertThat(loadForUser(userId).toSet()).isEmpty()
- }
-
- @Test
- fun unmarkAdded() =
- testScope.runTest {
- val userId = 0
- val specs = "a,custom(b/c)"
- storeForUser(specs, userId)
+ val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+ val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+ runCurrent()
- underTest.unmarkTileAdded(userId, TileSpec.create("a"))
+ underTest.markTileAdded(userId = 1, TileSpec.create("a"))
- assertThat(loadForUser(userId).toSet())
- .containsExactlyElementsIn(setOf(TileSpec.create("custom(b/c)")))
+ assertThat(tilesFromUser0).isEmpty()
+ assertThat(tilesFromUser1).containsExactlyElementsIn(setOf(TileSpec.create("a")))
}
@Test
@@ -159,33 +104,23 @@ class AutoAddSettingsRepositoryTest : SysuiTestCase() {
val specs = "a,b"
storeForUser(specs, 0)
storeForUser(specs, 1)
+ val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+ val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+ runCurrent()
underTest.unmarkTileAdded(1, TileSpec.create("a"))
- assertThat(loadForUser(0).toSet()).isEqualTo(specs.toSet())
- assertThat(loadForUser(1).toSet()).isEqualTo(setOf(TileSpec.create("b")))
+ assertThat(tilesFromUser0).isEqualTo(specs.toTilesSet())
+ assertThat(tilesFromUser1).isEqualTo(setOf(TileSpec.create("b")))
}
private fun storeForUser(specs: String, userId: Int) {
secureSettings.putStringForUser(SETTING, specs, userId)
}
- private fun loadForUser(userId: Int): String {
- return secureSettings.getStringForUser(SETTING, userId) ?: ""
- }
-
companion object {
private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
- private const val DELIMITER = ","
- fun Set<TileSpec>.toSeparatedString() = joinToString(DELIMITER, transform = TileSpec::spec)
-
- fun String.toSet(): Set<TileSpec> {
- return if (isNullOrBlank()) {
- emptySet()
- } else {
- split(DELIMITER).map(TileSpec::create).toSet()
- }
- }
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
new file mode 100644
index 000000000000..dc09a33adc1c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -0,0 +1,220 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.Intent
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@RoboPilotTest
+class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
+ private val dispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(dispatcher)
+
+ @Mock private lateinit var pipelineLogger: QSPipelineLogger
+
+ private lateinit var underTest: QSSettingsRestoredBroadcastRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ QSSettingsRestoredBroadcastRepository(
+ fakeBroadcastDispatcher,
+ pipelineLogger,
+ testScope.backgroundScope,
+ dispatcher,
+ )
+ }
+
+ @Test
+ fun restoreDataAfterBothIntents_tilesRestoredFirst() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ val autoAddIntent =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ // No restore data yet as we are missing one of the broadcasts
+ assertThat(restoreData).isNull()
+
+ // After the second event, we see the corresponding restore
+ sendIntentForUser(autoAddIntent, user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
+ @Test
+ fun restoreDataAfterBothIntents_autoAddRestoredFirst() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ val autoAddIntent =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+
+ sendIntentForUser(autoAddIntent, user)
+
+ // No restore data yet as we are missing one of the broadcasts
+ assertThat(restoreData).isNull()
+
+ // After the second event, we see the corresponding restore
+ sendIntentForUser(tilesIntent, user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
+ @Test
+ fun interleavedBroadcastsFromDifferentUsers_onlysendDataForCorrectUser() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+
+ val user0 = 0
+ val user10 = 10
+
+ val currentTiles10 = "z,y,x"
+ val restoredTiles10 = "x"
+ val currentAutoAdded10 = "f"
+ val restoredAutoAdded10 = "f,g"
+
+ val tilesIntent0 =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+ val autoAddIntent0 =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+ val tilesIntent10 =
+ createRestoreIntent(
+ RestoreType.TILES,
+ currentTiles10,
+ restoredTiles10,
+ )
+ val autoAddIntent10 =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ currentAutoAdded10,
+ restoredAutoAdded10,
+ )
+
+ sendIntentForUser(tilesIntent0, user0)
+ sendIntentForUser(autoAddIntent10, user10)
+ assertThat(restoreData).isNull()
+
+ sendIntentForUser(tilesIntent10, user10)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(restoredTiles10.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(restoredAutoAdded10.toTilesSet())
+ assertThat(userId).isEqualTo(user10)
+ }
+
+ sendIntentForUser(autoAddIntent0, user0)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+ assertThat(userId).isEqualTo(user0)
+ }
+ }
+
+ private fun sendIntentForUser(intent: Intent, userId: Int) {
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ intent,
+ FakeBroadcastDispatcher.fakePendingResultForUser(userId)
+ )
+ }
+
+ companion object {
+ private const val CURRENT_TILES = "a,b,c,d"
+ private const val RESTORED_TILES = "b,a,c"
+ private const val CURRENT_AUTO_ADDED_TILES = "d"
+ private const val RESTORED_AUTO_ADDED_TILES = "e"
+
+ private fun createRestoreIntent(
+ type: RestoreType,
+ previousValue: String,
+ restoredValue: String,
+ ): Intent {
+ val setting =
+ when (type) {
+ RestoreType.TILES -> Settings.Secure.QS_TILES
+ RestoreType.AUTOADD -> Settings.Secure.QS_AUTO_ADDED_TILES
+ }
+ return Intent(Intent.ACTION_SETTING_RESTORED)
+ .putExtra(Intent.EXTRA_SETTING_NAME, setting)
+ .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue)
+ .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue)
+ }
+
+ private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+
+ private enum class RestoreType {
+ TILES,
+ AUTOADD,
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 1c28e4c022a6..08adebb87b1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -23,14 +23,12 @@ import com.android.systemui.res.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.QSHost
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.retail.data.repository.FakeRetailModeRepository
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -49,9 +47,28 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
private lateinit var secureSettings: FakeSettings
private lateinit var retailModeRepository: FakeRetailModeRepository
+ private val defaultTilesRepository =
+ object : DefaultTilesRepository {
+ override val defaultTiles: List<TileSpec>
+ get() = DEFAULT_TILES.toTileSpecs()
+ }
@Mock private lateinit var logger: QSPipelineLogger
+ private val userTileSpecRepositoryFactory =
+ object : UserTileSpecRepository.Factory {
+ override fun create(userId: Int): UserTileSpecRepository {
+ return UserTileSpecRepository(
+ userId,
+ defaultTilesRepository,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+ }
+
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -66,293 +83,85 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
retailModeRepository.setRetailMode(false)
with(context.orCreateTestableResources) {
- addOverride(R.string.quick_settings_tiles_default, DEFAULT_TILES)
addOverride(R.string.quick_settings_tiles_retail_mode, RETAIL_TILES)
}
underTest =
TileSpecSettingsRepository(
- secureSettings,
context.resources,
logger,
retailModeRepository,
- testDispatcher,
+ userTileSpecRepositoryFactory
)
}
@Test
- fun emptySetting_usesDefaultValue() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
- assertThat(tiles).isEqualTo(getDefaultTileSpecs())
- }
-
- @Test
- fun changeInSettings_changesValue() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a", 0)
- assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
-
- storeTilesForUser("a,custom(b/c)", 0)
- assertThat(tiles)
- .isEqualTo(listOf(TileSpec.create("a"), TileSpec.create("custom(b/c)")))
- }
-
- @Test
fun tilesForCorrectUsers() =
testScope.runTest {
- val tilesFromUser0 by collectLastValue(underTest.tilesSpecs(0))
- val tilesFromUser1 by collectLastValue(underTest.tilesSpecs(1))
-
val user0Tiles = "a"
val user1Tiles = "custom(b/c)"
storeTilesForUser(user0Tiles, 0)
storeTilesForUser(user1Tiles, 1)
+ val tilesFromUser0 by collectLastValue(underTest.tilesSpecs(0))
+ val tilesFromUser1 by collectLastValue(underTest.tilesSpecs(1))
+
assertThat(tilesFromUser0).isEqualTo(user0Tiles.toTileSpecs())
assertThat(tilesFromUser1).isEqualTo(user1Tiles.toTileSpecs())
}
@Test
- fun invalidTilesAreNotPresent() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "d,custom(bad)"
- storeTilesForUser(specs, 0)
-
- assertThat(tiles).isEqualTo(specs.toTileSpecs().filter { it != TileSpec.Invalid })
- }
-
- @Test
- fun noValidTiles_defaultSet() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("custom(bad),custom()", 0)
-
- assertThat(tiles).isEqualTo(getDefaultTileSpecs())
- }
-
- @Test
- fun addTileAtEnd() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a", 0)
-
- underTest.addTile(userId = 0, TileSpec.create("b"))
-
- val expected = "a,b"
- assertThat(loadTilesForUser(0)).isEqualTo(expected)
- assertThat(tiles).isEqualTo(expected.toTileSpecs())
- }
-
- @Test
- fun addTileAtPosition() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a,custom(b/c)", 0)
-
- underTest.addTile(userId = 0, TileSpec.create("d"), position = 1)
-
- val expected = "a,d,custom(b/c)"
- assertThat(loadTilesForUser(0)).isEqualTo(expected)
- assertThat(tiles).isEqualTo(expected.toTileSpecs())
- }
-
- @Test
- fun addInvalidTile_noop() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
- storeTilesForUser(specs, 0)
-
- underTest.addTile(userId = 0, TileSpec.Invalid)
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun addTileAtPosition_tooLarge_addedAtEnd() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
- storeTilesForUser(specs, 0)
-
- underTest.addTile(userId = 0, TileSpec.create("d"), position = 100)
-
- val expected = "a,custom(b/c),d"
- assertThat(loadTilesForUser(0)).isEqualTo(expected)
- assertThat(tiles).isEqualTo(expected.toTileSpecs())
- }
-
- @Test
fun addTileForOtherUser_addedInThatUser() =
testScope.runTest {
- val tilesUser0 by collectLastValue(underTest.tilesSpecs(0))
- val tilesUser1 by collectLastValue(underTest.tilesSpecs(1))
-
storeTilesForUser("a", 0)
storeTilesForUser("b", 1)
+ val tilesUser0 by collectLastValue(underTest.tilesSpecs(0))
+ val tilesUser1 by collectLastValue(underTest.tilesSpecs(1))
+ runCurrent()
underTest.addTile(userId = 1, TileSpec.create("c"))
- assertThat(loadTilesForUser(0)).isEqualTo("a")
assertThat(tilesUser0).isEqualTo("a".toTileSpecs())
- assertThat(loadTilesForUser(1)).isEqualTo("b,c")
+ assertThat(loadTilesForUser(0)).isEqualTo("a")
assertThat(tilesUser1).isEqualTo("b,c".toTileSpecs())
- }
-
- @Test
- fun removeTiles() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a,b", 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("a")))
-
- assertThat(loadTilesForUser(0)).isEqualTo("b")
- assertThat(tiles).isEqualTo("b".toTileSpecs())
- }
-
- @Test
- fun removeTilesNotThere_noop() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,b"
- storeTilesForUser(specs, 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("c")))
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun removeInvalidTile_noop() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,b"
- storeTilesForUser(specs, 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.Invalid))
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTilesForUser(1)).isEqualTo("b,c")
}
@Test
fun removeTileFromSecondaryUser_removedOnlyInCorrectUser() =
testScope.runTest {
- val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
- val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
-
val specs = "a,b"
storeTilesForUser(specs, 0)
storeTilesForUser(specs, 1)
+ val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
+ val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
+ runCurrent()
underTest.removeTiles(userId = 1, listOf(TileSpec.create("a")))
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
assertThat(user0Tiles).isEqualTo(specs.toTileSpecs())
- assertThat(loadTilesForUser(1)).isEqualTo("b")
- assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
- }
-
- @Test
- fun removeMultipleTiles() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- storeTilesForUser("a,b,c,d", 0)
-
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("a"), TileSpec.create("c")))
-
- assertThat(loadTilesForUser(0)).isEqualTo("b,d")
- assertThat(tiles).isEqualTo("b,d".toTileSpecs())
- }
-
- @Test
- fun changeTiles() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
-
- underTest.setTiles(userId = 0, specs.toTileSpecs())
-
- assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun changeTiles_ignoresInvalid() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,custom(b/c)"
-
- underTest.setTiles(userId = 0, listOf(TileSpec.Invalid) + specs.toTileSpecs())
-
assertThat(loadTilesForUser(0)).isEqualTo(specs)
- assertThat(tiles).isEqualTo(specs.toTileSpecs())
- }
-
- @Test
- fun changeTiles_empty_noChanges() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- underTest.setTiles(userId = 0, emptyList())
-
- assertThat(loadTilesForUser(0)).isNull()
- assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+ assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTilesForUser(1)).isEqualTo("b")
}
@Test
fun changeTiles_forCorrectUser() =
testScope.runTest {
- val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
- val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
-
val specs = "a"
storeTilesForUser(specs, 0)
storeTilesForUser(specs, 1)
+ val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
+ val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
+ runCurrent()
underTest.setTiles(userId = 1, "b".toTileSpecs())
- assertThat(loadTilesForUser(0)).isEqualTo("a")
assertThat(user0Tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTilesForUser(0)).isEqualTo("a")
- assertThat(loadTilesForUser(1)).isEqualTo("b")
assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
- }
-
- @Test
- fun multipleConcurrentRemovals_bothRemoved() =
- testScope.runTest {
- val tiles by collectLastValue(underTest.tilesSpecs(0))
-
- val specs = "a,b,c"
- storeTilesForUser(specs, 0)
-
- coroutineScope {
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("c")))
- underTest.removeTiles(userId = 0, listOf(TileSpec.create("a")))
- }
-
- assertThat(loadTilesForUser(0)).isEqualTo("b")
- assertThat(tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTilesForUser(1)).isEqualTo("b")
}
@Test
@@ -361,6 +170,7 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
retailModeRepository.setRetailMode(true)
val tiles by collectLastValue(underTest.tilesSpecs(0))
+ runCurrent()
assertThat(tiles).isEqualTo(RETAIL_TILES.toTileSpecs())
}
@@ -369,25 +179,13 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
fun retailMode_cannotModifyTiles() =
testScope.runTest {
retailModeRepository.setRetailMode(true)
-
- underTest.setTiles(0, DEFAULT_TILES.toTileSpecs())
-
- assertThat(loadTilesForUser(0)).isNull()
- }
-
- @Test
- fun emptyTilesReplacedByDefaultInSettings() =
- testScope.runTest {
val tiles by collectLastValue(underTest.tilesSpecs(0))
runCurrent()
- assertThat(loadTilesForUser(0))
- .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
- }
+ underTest.setTiles(0, listOf(TileSpec.create("a")))
- private fun getDefaultTileSpecs(): List<TileSpec> {
- return QSHost.getDefaultSpecs(context.resources).map(TileSpec::create)
- }
+ assertThat(loadTilesForUser(0)).isEqualTo(DEFAULT_TILES)
+ }
private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
@@ -403,8 +201,6 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
private const val RETAIL_TILES = "d"
private const val SETTING = Settings.Secure.QS_TILES
- private fun String.toTileSpecs(): List<TileSpec> {
- return split(",").map(TileSpec::create)
- }
+ private fun String.toTileSpecs() = TilesSettingConverter.toTilesList(this)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
new file mode 100644
index 000000000000..20876237b476
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RoboPilotTest
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TilesSettingConverterTest : SysuiTestCase() {
+
+ @Test
+ fun toTilesList_correctContentAndOrdering() {
+ val specString =
+ listOf(
+ "c",
+ "b",
+ "custom(x/y)",
+ "d",
+ )
+ .joinToString(DELIMITER)
+
+ val expected =
+ listOf(
+ TileSpec.create("c"),
+ TileSpec.create("b"),
+ TileSpec.create("custom(x/y)"),
+ TileSpec.create("d"),
+ )
+
+ assertThat(TilesSettingConverter.toTilesList(specString)).isEqualTo(expected)
+ }
+
+ @Test
+ fun toTilesList_removesInvalid() {
+ val specString =
+ listOf(
+ "a",
+ "",
+ "b",
+ )
+ .joinToString(DELIMITER)
+ assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
+ val expected =
+ listOf(
+ TileSpec.create("a"),
+ TileSpec.create("b"),
+ )
+ assertThat(TilesSettingConverter.toTilesList(specString)).isEqualTo(expected)
+ }
+
+ @Test
+ fun toTilesSet_correctContent() {
+ val specString =
+ listOf(
+ "c",
+ "b",
+ "custom(x/y)",
+ "d",
+ )
+ .joinToString(DELIMITER)
+
+ val expected =
+ setOf(
+ TileSpec.create("c"),
+ TileSpec.create("b"),
+ TileSpec.create("custom(x/y)"),
+ TileSpec.create("d"),
+ )
+
+ assertThat(TilesSettingConverter.toTilesSet(specString)).isEqualTo(expected)
+ }
+
+ @Test
+ fun toTilesSet_removesInvalid() {
+ val specString =
+ listOf(
+ "a",
+ "",
+ "b",
+ )
+ .joinToString(DELIMITER)
+ assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
+ val expected =
+ setOf(
+ TileSpec.create("a"),
+ TileSpec.create("b"),
+ )
+ assertThat(TilesSettingConverter.toTilesSet(specString)).isEqualTo(expected)
+ }
+
+ companion object {
+ private const val DELIMITER = ","
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
new file mode 100644
index 000000000000..81fd72b11227
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
@@ -0,0 +1,160 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RoboPilotTest
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UserAutoAddRepositoryTest : SysuiTestCase() {
+ private val secureSettings = FakeSettings()
+
+ @Mock private lateinit var logger: QSPipelineLogger
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: UserAutoAddRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ UserAutoAddRepository(
+ USER,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+
+ @Test
+ fun nonExistentSetting_emptySet() =
+ testScope.runTest {
+ val specs by collectLastValue(underTest.autoAdded())
+
+ assertThat(specs).isEmpty()
+ }
+
+ @Test
+ fun settingsChange_noChanges() =
+ testScope.runTest {
+ val value = "a,custom(b/c)"
+ store(value)
+ val specs by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ assertThat(specs).isEqualTo(value.toTilesSet())
+
+ val newValue = "a"
+ store(newValue)
+
+ assertThat(specs).isEqualTo(value.toTilesSet())
+ }
+
+ @Test
+ fun noInvalidTileSpecs() =
+ testScope.runTest {
+ val specs = "d,custom(bad)"
+ store(specs)
+ val tiles by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ assertThat(tiles).isEqualTo("d".toTilesSet())
+ }
+
+ @Test
+ fun markAdded() =
+ testScope.runTest {
+ val specs = mutableSetOf(TileSpec.create("a"))
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ underTest.markTileAdded(TileSpec.create("a"))
+
+ assertThat(autoAdded).containsExactlyElementsIn(specs)
+
+ specs.add(TileSpec.create("b"))
+ underTest.markTileAdded(TileSpec.create("b"))
+
+ assertThat(autoAdded).containsExactlyElementsIn(specs)
+ }
+
+ @Test
+ fun markAdded_Invalid_noop() =
+ testScope.runTest {
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ underTest.markTileAdded(TileSpec.Invalid)
+
+ Truth.assertThat(autoAdded).isEmpty()
+ }
+
+ @Test
+ fun unmarkAdded() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ store(specs)
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ underTest.unmarkTileAdded(TileSpec.create("a"))
+
+ assertThat(autoAdded).containsExactlyElementsIn(setOf(TileSpec.create("custom(b/c)")))
+ }
+
+ @Test
+ fun restore_addsRestoredTiles() =
+ testScope.runTest {
+ val specs = "a,b"
+ val restored = "b,c"
+ store(specs)
+ val autoAdded by collectLastValue(underTest.autoAdded())
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ emptyList(),
+ restored.toTilesSet(),
+ USER,
+ )
+ underTest.reconcileRestore(restoreData)
+
+ assertThat(autoAdded).containsExactlyElementsIn("a,b,c".toTilesSet())
+ }
+
+ private fun store(specs: String) {
+ secureSettings.putStringForUser(SETTING, specs, USER)
+ }
+
+ companion object {
+ private const val USER = 10
+ private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
new file mode 100644
index 000000000000..389580c1326b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
@@ -0,0 +1,351 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RoboPilotTest
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class UserTileSpecRepositoryTest : SysuiTestCase() {
+ private val secureSettings = FakeSettings()
+ private val defaultTilesRepository =
+ object : DefaultTilesRepository {
+ override val defaultTiles: List<TileSpec>
+ get() = DEFAULT_TILES.toTileSpecs()
+ }
+
+ @Mock private lateinit var logger: QSPipelineLogger
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: UserTileSpecRepository
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ UserTileSpecRepository(
+ USER,
+ defaultTilesRepository,
+ secureSettings,
+ logger,
+ testScope.backgroundScope,
+ testDispatcher,
+ )
+ }
+
+ @Test
+ fun emptySetting_usesDefaultValue() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles())
+ assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+ }
+
+ @Test
+ fun changeInSettings_valueDoesntChange() =
+ testScope.runTest {
+ storeTiles("a")
+ val tiles by collectLastValue(underTest.tiles())
+
+ assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
+
+ storeTiles("a,custom(b/c)")
+ assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
+ }
+
+ @Test
+ fun changeInSettings_settingIsRestored() =
+ testScope.runTest {
+ storeTiles("a")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ storeTiles("a,custom(b/c)")
+ assertThat(loadTiles()).isEqualTo("a")
+ }
+
+ @Test
+ fun invalidTilesAreNotPresent() =
+ testScope.runTest {
+ val specs = "d,custom(bad)"
+ storeTiles(specs)
+
+ val tiles by collectLastValue(underTest.tiles())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs().filter { it != TileSpec.Invalid })
+ }
+
+ @Test
+ fun noValidTiles_defaultSet() =
+ testScope.runTest {
+ storeTiles("custom(bad),custom()")
+
+ val tiles by collectLastValue(underTest.tiles())
+
+ assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+ }
+
+ /*
+ * Following tests are for the possible actions that can be performed to the list of tiles.
+ * In general, the tests follow this scheme:
+ *
+ * 1. Set starting tiles in Settings
+ * 2. Start collection of flows
+ * 3. Call `runCurrent` so all collectors are started (side effects)
+ * 4. Perform operation
+ * 5. Check that the flow contains the right value
+ * 6. Check that settings contains the right value.
+ */
+
+ @Test
+ fun addTileAtEnd() =
+ testScope.runTest {
+ storeTiles("a")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.create("b"))
+
+ val expected = "a,b"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ @Test
+ fun addTileAtPosition() =
+ testScope.runTest {
+ storeTiles("a,custom(b/c)")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.create("d"), position = 1)
+
+ val expected = "a,d,custom(b/c)"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ @Test
+ fun addInvalidTile_noop() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.Invalid)
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun addTileAtPosition_tooLarge_addedAtEnd() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.addTile(TileSpec.create("d"), position = 100)
+
+ val expected = "a,custom(b/c),d"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ @Test
+ fun removeTiles() =
+ testScope.runTest {
+ storeTiles("a,b")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.create("a")))
+
+ assertThat(tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTiles()).isEqualTo("b")
+ }
+
+ @Test
+ fun removeTilesNotThere_noop() =
+ testScope.runTest {
+ val specs = "a,b"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.create("c")))
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun removeInvalidTile_noop() =
+ testScope.runTest {
+ val specs = "a,b"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.Invalid))
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun removeMultipleTiles() =
+ testScope.runTest {
+ storeTiles("a,b,c,d")
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.removeTiles(listOf(TileSpec.create("a"), TileSpec.create("c")))
+
+ assertThat(tiles).isEqualTo("b,d".toTileSpecs())
+ assertThat(loadTiles()).isEqualTo("b,d")
+ }
+
+ @Test
+ fun changeTiles() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.setTiles(specs.toTileSpecs())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun changeTiles_ignoresInvalid() =
+ testScope.runTest {
+ val specs = "a,custom(b/c)"
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.setTiles(listOf(TileSpec.Invalid) + specs.toTileSpecs())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun changeTiles_empty_noChanges() =
+ testScope.runTest {
+ val specs = "a,b,c,d"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ underTest.setTiles(emptyList())
+
+ assertThat(tiles).isEqualTo(specs.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(specs)
+ }
+
+ @Test
+ fun multipleConcurrentRemovals_bothRemoved() =
+ testScope.runTest {
+ val specs = "a,b,c"
+ storeTiles(specs)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ coroutineScope {
+ underTest.removeTiles(listOf(TileSpec.create("c")))
+ underTest.removeTiles(listOf(TileSpec.create("a")))
+ }
+
+ assertThat(tiles).isEqualTo("b".toTileSpecs())
+ assertThat(loadTiles()).isEqualTo("b")
+ }
+
+ @Test
+ fun emptyTilesReplacedByDefaultInSettings() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ assertThat(loadTiles())
+ .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
+ }
+
+ @Test
+ fun restoreDataIsProperlyReconciled() =
+ testScope.runTest {
+ // Tile b was just auto-added, so we should re-add it in position 1
+ // Tile e was auto-added before, but the user had removed it (not in the restored set).
+ // It should not be re-added
+ val specsBeforeRestore = "a,b,c,d,e"
+ val restoredSpecs = "a,c,d,f"
+ val autoAddedBeforeRestore = "b,d"
+ val restoredAutoAdded = "d,e"
+
+ storeTiles(specsBeforeRestore)
+ val tiles by collectLastValue(underTest.tiles())
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredSpecs.toTileSpecs(),
+ restoredAutoAdded.toTilesSet(),
+ USER,
+ )
+ underTest.reconcileRestore(restoreData, autoAddedBeforeRestore.toTilesSet())
+ runCurrent()
+
+ val expected = "a,b,c,d,f"
+ assertThat(tiles).isEqualTo(expected.toTileSpecs())
+ assertThat(loadTiles()).isEqualTo(expected)
+ }
+
+ private fun getDefaultTileSpecs(): List<TileSpec> {
+ return defaultTilesRepository.defaultTiles
+ }
+
+ private fun TestScope.storeTiles(specs: String) {
+ secureSettings.putStringForUser(SETTING, specs, USER)
+ runCurrent()
+ }
+
+ private fun loadTiles(): String? {
+ return secureSettings.getStringForUser(SETTING, USER)
+ }
+
+ companion object {
+ private const val USER = 10
+ private const val DEFAULT_TILES = "a,b,c"
+ private const val SETTING = Settings.Secure.QS_TILES
+
+ private fun String.toTileSpecs() = TilesSettingConverter.toTilesList(this)
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index dc1b9c4d0d14..a7505240caeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -46,6 +46,7 @@ import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -91,6 +92,8 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
@Mock private lateinit var logger: QSPipelineLogger
+ @Mock private lateinit var newQSTileFactory: NewQSTileFactory
+
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -105,6 +108,8 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true)
featureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, true)
+ // TODO(b/299909337): Add test checking the new factory is used when the flag is on
+ featureFlags.set(Flags.QS_PIPELINE_NEW_TILES, true)
userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1))
@@ -117,6 +122,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
userRepository = userRepository,
customTileStatePersister = customTileStatePersister,
tileFactory = tileFactory,
+ newQSTileFactory = { newQSTileFactory },
customTileAddedRepository = customTileAddedRepository,
tileLifecycleManagerFactory = tileLifecycleManagerFactory,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
new file mode 100644
index 000000000000..2e6b50b637dd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -0,0 +1,94 @@
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeQSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
+import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class RestoreReconciliationInteractorTest : SysuiTestCase() {
+
+ private val tileSpecRepository = FakeTileSpecRepository()
+ private val autoAddRepository = FakeAutoAddRepository()
+
+ private val qsSettingsRestoredRepository = FakeQSSettingsRestoredRepository()
+
+ private lateinit var underTest: RestoreReconciliationInteractor
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ RestoreReconciliationInteractor(
+ tileSpecRepository,
+ autoAddRepository,
+ qsSettingsRestoredRepository,
+ testScope.backgroundScope,
+ testDispatcher
+ )
+ underTest.start()
+ }
+
+ @Test
+ fun reconciliationInCorrectOrder_hascurrentAutoAdded() =
+ testScope.runTest {
+ val user = 10
+ val tiles by collectLastValue(tileSpecRepository.tilesSpecs(user))
+ val autoAdd by collectLastValue(autoAddRepository.autoAddedTiles(user))
+
+ // Tile b was just auto-added, so we should re-add it in position 1
+ // Tile e was auto-added before, but the user had removed it (not in the restored set).
+ // It should not be re-added
+ val specsBeforeRestore = "a,b,c,d,e"
+ val restoredSpecs = "a,c,d,f"
+ val autoAddedBeforeRestore = "b,d"
+ val restoredAutoAdded = "d,e"
+
+ val restoreData =
+ RestoreData(
+ restoredSpecs.toTilesList(),
+ restoredAutoAdded.toTilesSet(),
+ user,
+ )
+
+ autoAddedBeforeRestore.toTilesSet().forEach {
+ autoAddRepository.markTileAdded(user, it)
+ }
+ tileSpecRepository.setTiles(user, specsBeforeRestore.toTilesList())
+
+ qsSettingsRestoredRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ val expectedTiles = "a,b,c,d,f"
+ assertThat(tiles).isEqualTo(expectedTiles.toTilesList())
+
+ val expectedAutoAdd = "b,d,e"
+ assertThat(autoAdd).isEqualTo(expectedAutoAdd.toTilesSet())
+ }
+
+ companion object {
+ private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+ private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index bce4c06d320f..3bf59ca62024 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -172,6 +172,9 @@ class DeviceControlsTileTest : SysuiTestCase() {
@Test
fun testNotAvailableControls() {
featureEnabled = false
+
+ // Destroy previous tile
+ tile.destroy()
tile = createTile()
assertThat(tile.isAvailable).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index a0c107340dcd..954d30edf143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -226,6 +226,10 @@ public class DreamTileTest extends SysuiTestCase {
assertTrue(supportedTileOnlySystemUser.isAvailable());
when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo);
assertFalse(supportedTileOnlySystemUser.isAvailable());
+
+ destroyTile(unsupportedTile);
+ destroyTile(supportedTileAllUsers);
+ destroyTile(supportedTileOnlySystemUser);
}
@Test
@@ -250,6 +254,8 @@ public class DreamTileTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
dockedTile.getState().icon);
+
+ destroyTile(dockedTile);
}
private void setScreensaverEnabled(boolean enabled) {
@@ -257,6 +263,11 @@ public class DreamTileTest extends SysuiTestCase {
DEFAULT_USER);
}
+ private void destroyTile(QSTileImpl<?> tile) {
+ tile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
private DreamTile constructTileForTest(boolean dreamSupported,
boolean dreamOnlyEnabledForSystemUser) {
return new DreamTile(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index df6993d11447..440270b6ebfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -216,7 +216,7 @@ public class RotationLockTileTest extends SysuiTestCase {
public void testSecondaryString_rotationResolverDisabled_isEmpty() {
mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver,
false);
- mLockTile = new RotationLockTile(
+ RotationLockTile otherTile = new RotationLockTile(
mHost,
mUiEventLogger,
mTestableLooper.getLooper(),
@@ -232,10 +232,12 @@ public class RotationLockTileTest extends SysuiTestCase {
new FakeSettings()
);
- mLockTile.refreshState();
+ otherTile.refreshState();
mTestableLooper.processAllMessages();
- assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ assertEquals("", otherTile.getState().secondaryLabel.toString());
+
+ destroyTile(otherTile);
}
@Test
@@ -258,6 +260,12 @@ public class RotationLockTileTest extends SysuiTestCase {
assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
}
+
+ private void destroyTile(QSTileImpl<?> tile) {
+ tile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
private void enableAutoRotation() {
when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
index 47b4244e0910..077c81343c83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
@@ -6,7 +6,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserActionHandler
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,7 +33,7 @@ class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
fun testPassesIntentToStarter() {
val intent = Intent("test.ACTION")
- underTest.handle(QSTileUserAction.Click(context, null), intent)
+ underTest.handle(null, intent)
verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 643866e3cade..eacb08010159 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -1,11 +1,14 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import android.graphics.drawable.ShapeDrawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.MediumTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
@@ -71,20 +74,21 @@ class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
fakeQSTileUserActionInteractor,
fakeQSTileDataInteractor,
object : QSTileDataToStateMapper<Any> {
- override fun map(config: QSTileConfig, data: Any): QSTileState {
- return QSTileState(config.tileIcon, config.tileLabel)
- }
+ override fun map(config: QSTileConfig, data: Any): QSTileState =
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
},
testCoroutineDispatcher,
tileScope = scope.backgroundScope,
) {}
private companion object {
+
val TEST_QS_TILE_CONFIG =
QSTileConfig(
TileSpec.create("default"),
- Icon.createWithContentUri(""),
- "",
+ Icon.Loaded(ShapeDrawable(), null),
+ 0,
+ InstanceId.fakeInstanceId(0),
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 59b595393749..a2aed988a423 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -41,7 +41,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.MediaProjectionCaptureTarget;
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index d470d24489af..3ae1f35b8134 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -22,11 +22,13 @@ import android.testing.TestableLooper
import android.view.View
import android.widget.Spinner
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
+import com.android.systemui.mediaprojection.permission.SINGLE_APP
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -35,8 +37,8 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
new file mode 100644
index 000000000000..091531e435e4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.media.MediaPlayer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import java.lang.IllegalStateException
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@SmallTest
+class ScreenshotSoundControllerTest : SysuiTestCase() {
+
+ private val soundProvider = mock<ScreenshotSoundProvider>()
+ private val mediaPlayer = mock<MediaPlayer>()
+ private val bgDispatcher = UnconfinedTestDispatcher()
+ private val scope = TestScope(bgDispatcher)
+ @Before
+ fun setup() {
+ whenever(soundProvider.getScreenshotSound()).thenReturn(mediaPlayer)
+ }
+
+ @Test
+ fun init_soundLoading() {
+ createController()
+ bgDispatcher.scheduler.runCurrent()
+
+ verify(soundProvider).getScreenshotSound()
+ }
+
+ @Test
+ fun init_soundLoadingException_playAndReleaseDoNotThrow() = runTest {
+ whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
+
+ val controller = createController()
+
+ controller.playCameraSound().await()
+ controller.releaseScreenshotSound().await()
+
+ verify(mediaPlayer, never()).start()
+ verify(mediaPlayer, never()).release()
+ }
+
+ @Test
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = runTest {
+ val controller = createController()
+
+ controller.playCameraSound().await()
+
+ verify(mediaPlayer).start()
+ }
+
+ @Test
+ fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
+ val controller = createController()
+
+ controller.releaseScreenshotSound().await()
+
+ verify(mediaPlayer).release()
+ }
+
+ private fun createController() =
+ ScreenshotSoundControllerImpl(soundProvider, scope, bgDispatcher)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 31c8a3d77d6b..ed731dd41cd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -118,7 +118,7 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -291,7 +291,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock protected ShadeTransitionController mShadeTransitionController;
@Mock protected QS mQs;
- @Mock protected QSFragment mQSFragment;
+ @Mock protected QSFragmentLegacy mQSFragment;
@Mock protected ViewGroup mQsHeader;
@Mock protected ViewParent mViewParent;
@Mock protected ViewTreeObserver mViewTreeObserver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
index f7d2497d0bbf..0c3af03da59e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -22,9 +22,9 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.QSFragment
+import com.android.systemui.qs.QSFragmentLegacy
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -40,7 +40,7 @@ class NotificationsQuickSettingsContainerTest : SysuiTestCase() {
@Mock private lateinit var qsFrame: View
@Mock private lateinit var stackScroller: View
@Mock private lateinit var keyguardStatusBar: View
- @Mock private lateinit var qsFragment: QSFragment
+ @Mock private lateinit var qsFragment: QSFragmentLegacy
private lateinit var qsView: ViewGroup
private lateinit var qsContainer: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index fb0d4db4840a..8138b328a3c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -34,7 +34,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.dump.DumpManager;
@@ -46,7 +45,8 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
+import com.android.systemui.res.R;
import com.android.systemui.scene.SceneTestUtils;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
@@ -75,18 +75,17 @@ import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.user.domain.interactor.UserInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
-import dagger.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import dagger.Lazy;
import kotlinx.coroutines.test.TestScope;
public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@@ -109,7 +108,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardBottomAreaView mQsFrame;
@Mock protected KeyguardStatusBarView mKeyguardStatusBar;
@Mock protected QS mQs;
- @Mock protected QSFragment mQSFragment;
+ @Mock protected QSFragmentLegacy mQSFragment;
@Mock protected Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
@Mock protected NotificationPanelViewController mNotificationPanelViewController;
@Mock protected NotificationPanelView mPanelView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index d018cbbfbc24..2be1c09843c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -25,7 +25,6 @@ import android.os.UserManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
@@ -35,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -54,6 +54,7 @@ import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -153,6 +154,7 @@ class ShadeInteractorTest : SysuiTestCase() {
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
underTest =
ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 48665fe0c9b0..e71473681211 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.shared.clocks
+import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.graphics.drawable.Drawable
@@ -28,12 +29,11 @@ import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.ClockSettings
-import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginLifecycleManager
+import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import kotlinx.coroutines.CoroutineDispatcher
@@ -46,6 +46,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -66,7 +67,6 @@ class ClockRegistryTest : SysuiTestCase() {
@Mock private lateinit var mockDefaultClock: ClockController
@Mock private lateinit var mockThumbnail: Drawable
@Mock private lateinit var mockContentResolver: ContentResolver
- @Mock private lateinit var mockPluginLifecycle: PluginLifecycleManager<ClockProviderPlugin>
private lateinit var fakeDefaultProvider: FakeClockPlugin
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry
@@ -84,6 +84,41 @@ class ClockRegistryTest : SysuiTestCase() {
}
}
+ private class FakeLifecycle(
+ private val tag: String,
+ private val plugin: ClockProviderPlugin?,
+ ) : PluginLifecycleManager<ClockProviderPlugin> {
+ var onLoad: (() -> Unit)? = null
+ var onUnload: (() -> Unit)? = null
+
+ private var mIsLoaded: Boolean = true
+ override fun isLoaded() = mIsLoaded
+ override fun getPlugin(): ClockProviderPlugin? = if (isLoaded) plugin else null
+
+ var mComponentName = ComponentName("Package[$tag]", "Class[$tag]")
+ override fun toString() = "Manager[$tag]"
+ override fun getPackage(): String = mComponentName.getPackageName()
+ override fun getComponentName(): ComponentName = mComponentName
+
+ private var isDebug: Boolean = false
+ override fun getIsDebug(): Boolean = isDebug
+ override fun setIsDebug(value: Boolean) { isDebug = value }
+
+ override fun loadPlugin() {
+ if (!mIsLoaded) {
+ mIsLoaded = true
+ onLoad?.invoke()
+ }
+ }
+
+ override fun unloadPlugin() {
+ if (mIsLoaded) {
+ mIsLoaded = false
+ onUnload?.invoke()
+ }
+ }
+ }
+
private class FakeClockPlugin : ClockProviderPlugin {
private val metadata = mutableListOf<ClockMetadata>()
private val createCallbacks = mutableMapOf<ClockId, (ClockId) -> ClockController>()
@@ -150,13 +185,15 @@ class ClockRegistryTest : SysuiTestCase() {
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = FakeLifecycle("1", plugin1)
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3")
.addClock("clock_4", "clock 4")
+ val lifecycle2 = FakeLifecycle("2", plugin2)
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val list = registry.getClocks()
assertEquals(
list.toSet(),
@@ -178,18 +215,18 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin2 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val list = registry.getClocks()
assertEquals(
list.toSet(),
@@ -204,8 +241,8 @@ class ClockRegistryTest : SysuiTestCase() {
assertEquals(registry.createExampleClock("clock_2"), mockClock)
assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
- verify(mockPluginLifecycle1, never()).unloadPlugin()
- verify(mockPluginLifecycle2, times(2)).unloadPlugin()
+ verify(lifecycle1, never()).unloadPlugin()
+ verify(lifecycle2, times(2)).unloadPlugin()
}
@Test
@@ -213,14 +250,16 @@ class ClockRegistryTest : SysuiTestCase() {
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
val clock = registry.createCurrentClock()
assertEquals(mockClock, clock)
@@ -231,17 +270,19 @@ class ClockRegistryTest : SysuiTestCase() {
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
assertEquals(DEFAULT_CLOCK_ID, registry.activeClockId)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
assertEquals("clock_3", registry.activeClockId)
}
@@ -250,15 +291,17 @@ class ClockRegistryTest : SysuiTestCase() {
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3")
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
- pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
+ pluginListener.onPluginUnloaded(plugin2, lifecycle2)
val clock = registry.createCurrentClock()
assertEquals(clock, mockDefaultClock)
@@ -266,15 +309,15 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun pluginRemoved_clockAndListChanged() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("clock_2", "clock 2")
+ val lifecycle1 = spy(FakeLifecycle("1", plugin1))
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin2 = FakeClockPlugin()
.addClock("clock_3", "clock 3", { mockClock })
.addClock("clock_4", "clock 4")
+ val lifecycle2 = spy(FakeLifecycle("2", plugin2))
var changeCallCount = 0
var listChangeCallCount = 0
@@ -288,32 +331,32 @@ class ClockRegistryTest : SysuiTestCase() {
assertEquals(1, changeCallCount)
assertEquals(0, listChangeCallCount)
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(1, listChangeCallCount)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
scheduler.runCurrent()
assertEquals(2, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginUnloaded(plugin1, mockPluginLifecycle1)
+ pluginListener.onPluginUnloaded(plugin1, lifecycle1)
scheduler.runCurrent()
assertEquals(2, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginUnloaded(plugin2, mockPluginLifecycle2)
+ pluginListener.onPluginUnloaded(plugin2, lifecycle2)
scheduler.runCurrent()
assertEquals(3, changeCallCount)
assertEquals(2, listChangeCallCount)
- pluginListener.onPluginDetached(mockPluginLifecycle1)
+ pluginListener.onPluginDetached(lifecycle1)
scheduler.runCurrent()
assertEquals(3, changeCallCount)
assertEquals(3, listChangeCallCount)
- pluginListener.onPluginDetached(mockPluginLifecycle2)
+ pluginListener.onPluginDetached(lifecycle2)
scheduler.runCurrent()
assertEquals(3, changeCallCount)
assertEquals(4, listChangeCallCount)
@@ -321,8 +364,9 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun unknownPluginAttached_clockAndListUnchanged_loadRequested() {
- val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle.getPackage()).thenReturn("some.other.package")
+ val lifecycle = FakeLifecycle("", null).apply {
+ mComponentName = ComponentName("some.other.package", "SomeClass")
+ }
var changeCallCount = 0
var listChangeCallCount = 0
@@ -331,7 +375,7 @@ class ClockRegistryTest : SysuiTestCase() {
override fun onAvailableClocksChanged() { listChangeCallCount++ }
})
- assertEquals(true, pluginListener.onPluginAttached(mockPluginLifecycle))
+ assertEquals(true, pluginListener.onPluginAttached(lifecycle))
scheduler.runCurrent()
assertEquals(0, changeCallCount)
assertEquals(0, listChangeCallCount)
@@ -339,10 +383,12 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun knownPluginAttached_clockAndListChanged_notLoaded() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.clocks.metro")
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.clocks.bignum")
+ val lifecycle1 = FakeLifecycle("Metro", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.metro", "MetroClock")
+ }
+ val lifecycle2 = FakeLifecycle("BigNum", null).apply {
+ mComponentName = ComponentName("com.android.systemui.clocks.bignum", "BigNumClock")
+ }
var changeCallCount = 0
var listChangeCallCount = 0
@@ -356,12 +402,12 @@ class ClockRegistryTest : SysuiTestCase() {
assertEquals(1, changeCallCount)
assertEquals(0, listChangeCallCount)
- assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle1))
+ assertEquals(false, pluginListener.onPluginAttached(lifecycle1))
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(1, listChangeCallCount)
- assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle2))
+ assertEquals(false, pluginListener.onPluginAttached(lifecycle2))
scheduler.runCurrent()
assertEquals(1, changeCallCount)
assertEquals(2, listChangeCallCount)
@@ -369,18 +415,14 @@ class ClockRegistryTest : SysuiTestCase() {
@Test
fun pluginAddRemove_concurrentModification() {
- val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- val mockPluginLifecycle3 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
- val mockPluginLifecycle4 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+ val lifecycle1 = FakeLifecycle("1", plugin1)
val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+ val lifecycle2 = FakeLifecycle("2", plugin2)
val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+ val lifecycle3 = FakeLifecycle("3", plugin3)
val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
- whenever(mockPluginLifecycle1.isLoaded).thenReturn(true)
- whenever(mockPluginLifecycle2.isLoaded).thenReturn(true)
- whenever(mockPluginLifecycle3.isLoaded).thenReturn(true)
- whenever(mockPluginLifecycle4.isLoaded).thenReturn(true)
+ val lifecycle4 = FakeLifecycle("4", plugin4)
// Set the current clock to the final clock to load
registry.applySettings(ClockSettings("clock_4", null))
@@ -390,15 +432,15 @@ class ClockRegistryTest : SysuiTestCase() {
// unload other plugins. This causes ClockRegistry to modify the list of available clock
// plugins while it is being iterated over. In production this happens as a result of a
// thread race, instead of synchronously like it does here.
- whenever(mockPluginLifecycle2.unloadPlugin()).then {
- pluginListener.onPluginDetached(mockPluginLifecycle1)
- pluginListener.onPluginLoaded(plugin4, mockContext, mockPluginLifecycle4)
+ lifecycle2.onUnload = {
+ pluginListener.onPluginDetached(lifecycle1)
+ pluginListener.onPluginLoaded(plugin4, mockContext, lifecycle4)
}
// Load initial plugins
- pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
- pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
- pluginListener.onPluginLoaded(plugin3, mockContext, mockPluginLifecycle3)
+ pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, lifecycle2)
+ pluginListener.onPluginLoaded(plugin3, mockContext, lifecycle3)
// Repeatedly verify the loaded providers to get final state
registry.verifyLoadedProviders()
@@ -484,11 +526,11 @@ class ClockRegistryTest : SysuiTestCase() {
private fun testTransitClockFlag(flag: Boolean) {
featureFlags.set(TRANSIT_CLOCK, flag)
registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
- val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val plugin = FakeClockPlugin()
.addClock("clock_1", "clock 1")
.addClock("DIGITAL_CLOCK_METRO", "metro clock")
- pluginListener.onPluginLoaded(plugin, mockContext, mockPluginLifecycle)
+ val lifecycle = FakeLifecycle("metro", plugin)
+ pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)
val list = registry.getClocks()
if (flag) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 1592c3007ec8..a6180ec8ea0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -248,7 +248,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
}
@Test
- public void testUpdateIconScale_smallerFontAndConstrainedDrawableSizeLessThanDpIconSize() {
+ public void testUpdateIconScale_smallerFontAndRawDrawableSizeLessThanDpIconSize() {
int dpIconSize = 60;
int dpDrawingSize = 30;
// smaller font scaling causes the spIconSize < dpIconSize
@@ -262,12 +262,42 @@ public class StatusBarIconViewTest extends SysuiTestCase {
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
mIconView.maybeUpdateIconScaleDimens();
- // WHEN both the constrained drawable width/height are less than dpIconSize,
+ // WHEN both the raw/constrained drawable width/height are less than dpIconSize,
+ // THEN the icon is scaled up from constrained drawable size to the raw drawable size
+ float scaleToBackRawDrawableSize = (float) 50 / 40;
// THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize
float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize;
// THEN the scaled icon should be scaled down further to fit spIconSize
float scaleToFitSpIconSize = (float) spIconSize / dpIconSize;
- assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, mIconView.getIconScale(), 0.01f);
+ assertEquals(scaleToBackRawDrawableSize * scaleToFitDrawingSize * scaleToFitSpIconSize,
+ mIconView.getIconScale(), 0.01f);
+ }
+
+ @Test
+ public void testUpdateIconScale_smallerFontAndConstrainedDrawableSizeLessThanDpIconSize() {
+ int dpIconSize = 60;
+ int dpDrawingSize = 30;
+ // smaller font scaling causes the spIconSize < dpIconSize
+ int spIconSize = 40;
+ // the icon view layout size would be 40x150
+ // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
+ setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
+ mIconView.setNotification(mock(StatusBarNotification.class));
+ // the raw drawable size is 70x70. When put the drawable into iconView whose
+ // layout size is 40x150, the drawable size would be constrained to 40x40
+ setIconDrawableWithSize(/* width= */ 70, /* height= */ 70);
+ mIconView.maybeUpdateIconScaleDimens();
+
+ // WHEN the raw drawable width/height are larger than dpIconSize,
+ // but the constrained drawable width/height are less than dpIconSize,
+ // THEN the icon is scaled up from constrained drawable size to fit dpIconSize
+ float scaleToFitDpIconSize = (float) dpIconSize / 40;
+ // THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize
+ float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize;
+ // THEN the scaled icon should be scaled down further to fit spIconSize
+ float scaleToFitSpIconSize = (float) spIconSize / dpIconSize;
+ assertEquals(scaleToFitDpIconSize * scaleToFitDrawingSize * scaleToFitSpIconSize,
+ mIconView.getIconScale(), 0.01f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index ac2aec6e0c86..0a10b2c85ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.never
@@ -126,6 +127,22 @@ class GroupExpansionManagerTest : SysuiTestCase() {
}
@Test
+ fun testExpandUnattachedEntry() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ // First, expand the entry when it is attached.
+ gem.setGroupExpanded(summary1, true)
+ assertThat(gem.isGroupExpanded(summary1)).isTrue()
+
+ // Un-attach it, and un-expand it.
+ NotificationEntryBuilder.setNewParent(summary1, null)
+ gem.setGroupExpanded(summary1, false)
+
+ // Expanding again should throw.
+ assertThrows(IllegalArgumentException::class.java) { gem.setGroupExpanded(summary1, true) }
+ }
+
+ @Test
fun testSyncWithPipeline() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
gem.attach(pipeline)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
index 37ec0e09f841..c1ffa641c6a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
@@ -58,6 +58,35 @@ class GroupMembershipManagerTest : SysuiTestCase() {
val noParentEntry = NotificationEntryBuilder().setParent(null).build()
assertThat(gmm.isChildInGroup(noParentEntry)).isFalse()
}
+ @Test
+ fun testIsChildInGroup_summary_old() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+ assertThat(gmm.isChildInGroup(summary)).isTrue()
+ }
+
+ @Test
+ fun testIsChildInGroup_summary_new() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+ assertThat(gmm.isChildInGroup(summary)).isFalse()
+ }
@Test
fun testIsChildInGroup_child() {
@@ -67,40 +96,78 @@ class GroupMembershipManagerTest : SysuiTestCase() {
}
@Test
- fun testIsGroupSummary() {
+ fun testIsGroupSummary_topLevelEntry() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
- val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build()
- assertThat(gmm.isGroupSummary(entry)).isTrue()
+ val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
+ assertThat(gmm.isGroupSummary(entry)).isFalse()
}
@Test
- fun testGetGroupSummary() {
+ fun testIsGroupSummary_summary() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+ val groupKey = "group"
val summary =
NotificationEntryBuilder()
- .setGroup(mContext, "group")
+ .setGroup(mContext, groupKey)
.setGroupSummary(mContext, true)
.build()
- val groupEntry =
- GroupEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).setSummary(summary).build()
- val entry =
- NotificationEntryBuilder().setGroup(mContext, "group").setParent(groupEntry).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
- assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary)
+ assertThat(gmm.isGroupSummary(summary)).isTrue()
}
@Test
- fun testGetGroupSummary_isSummary_old() {
- featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
- val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build()
+ fun testIsGroupSummary_child() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+ assertThat(gmm.isGroupSummary(entry)).isFalse()
+ }
+
+ @Test
+ fun testGetGroupSummary_topLevelEntry() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+ val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
assertThat(gmm.getGroupSummary(entry)).isNull()
}
@Test
- fun testGetGroupSummary_isSummary_new() {
+ fun testGetGroupSummary_summary() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+ assertThat(gmm.getGroupSummary(summary)).isEqualTo(summary)
+ }
+
+ @Test
+ fun testGetGroupSummary_child() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
- val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build()
- assertThat(gmm.getGroupSummary(entry)).isEqualTo(entry)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+ assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index f05436f66d82..50ce265b67d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -117,6 +118,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
PendingIntent mPendingIntent;
@Mock
UserTracker mUserTracker;
+ @Mock
+ DeviceProvisionedController mDeviceProvisionedController;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -141,7 +144,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
mFlags,
mKeyguardNotificationVisibilityProvider,
mUiEventLoggerFake,
- mUserTracker);
+ mUserTracker,
+ mDeviceProvisionedController);
mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
@@ -694,6 +698,25 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
}
@Test
+ public void testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned() {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
+
+ assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
+ .isEqualTo(FullScreenIntentDecision.FSI_NOT_PROVISIONED);
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "FSI_NOT_PROVISIONED");
+ }
+
+ @Test
public void testShouldNotFullScreen_willHun() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 64d025628754..7dcbd8084594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -73,14 +73,14 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
controller = ChannelEditorDialogController(mContext, mockNoMan, dialogBuilder)
channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
- channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_DEFAULT)
+ channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_NONE)
channelDefault = NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME)
- `when`(mockNoMan.getNotificationChannelGroupsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean()))
+ `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(ParceledListSlice(listOf(group)))
`when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
@@ -89,11 +89,13 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
@Test
fun testPrepareDialogForApp_noExtraChannels() {
+ channel1.group = group.id
+ channel2.group = group.id
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
- assertEquals(2, controller.paddedChannels.size)
+ assertEquals(2, controller.channelList.size)
}
@Test
@@ -101,39 +103,41 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
group.addChannel(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channelDefault), appIcon, clickListener)
+ channelDefault, appIcon, clickListener)
assertEquals("No channels should be shown when there is only the miscellaneous channel",
- 0, controller.paddedChannels.size)
+ 0, controller.channelList.size)
}
@Test
- fun testPrepareDialogForApp_noProvidedChannels_noException() {
- group.channels = listOf()
-
- controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(), appIcon, clickListener)
- }
-
- @Test
- fun testPrepareDialogForApp_retrievesUpTo4Channels() {
+ fun testPrepareDialogForApp_AddsAllChannelsAllGroups() {
+ val group2 = NotificationChannelGroup("two", "group two")
val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT)
+ channel3.group = group2.id
val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT)
+ channel4.group = group.id
+ val channel5 = NotificationChannel("test_channel_5", "Test channel 5", IMPORTANCE_DEFAULT)
+ channel5.group = group.id
- group.channels = listOf(channel1, channel2, channel3, channel4)
+ `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+ .thenReturn(ParceledListSlice(listOf(group, group2)))
+
+ group.channels = listOf(channel1, channel2, channel4, channel5)
+ group2.channels = listOf(channel3)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1), appIcon, clickListener)
+ channel1, appIcon, clickListener)
- assertEquals("ChannelEditorDialog should fetch enough channels to show 4",
- 4, controller.paddedChannels.size)
+ assertEquals("ChannelEditorDialog should show all channels",
+ 5, controller.channelList.size)
}
@Test
fun testApply_demoteChannel() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
// propose an adjustment of channel1
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -145,14 +149,33 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
// Channel 2 shouldn't have changed
assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_NONE, channel2.importance)
+ }
+
+ @Test
+ fun testApply_promoteChannel() {
+ group.channels = listOf(channel1, channel2)
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ channel1, appIcon, clickListener)
+
+ // propose an adjustment of channel1
+ controller.proposeEditForChannel(channel2, IMPORTANCE_DEFAULT)
+
+ controller.apply()
+
+ assertEquals("Proposed edits should take effect after apply",
IMPORTANCE_DEFAULT, channel2.importance)
+
+ // Channel 1 shouldn't have changed
+ assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_DEFAULT, channel1.importance)
}
@Test
fun testApply_demoteApp() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
controller.proposeSetAppNotificationsEnabled(false)
controller.apply()
@@ -168,7 +191,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
.thenReturn(false)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
controller.proposeSetAppNotificationsEnabled(true)
controller.apply()
@@ -181,7 +204,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
// GIVEN editor dialog
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN user taps settings
// Pass in any old view, it should never actually be used
@@ -197,7 +220,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN the user proposes a change
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -214,7 +237,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN the user proposes a change
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -236,7 +259,6 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
const val TEST_PACKAGE_NAME = "test_package"
const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME
const val TEST_UID = 1
- const val MULTIPLE_CHANNEL_COUNT = 2
const val TEST_CHANNEL = "test_channel"
const val TEST_CHANNEL_NAME = "Test Channel Name"
const val TEST_CHANNEL2 = "test_channel2"
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 ac8b42afd4b2..e37314301e28 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
@@ -481,33 +481,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
- public void testGetNumUniqueChildren_defaultChannel() throws Exception {
- ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
-
- assertEquals(1, groupRow.getNumUniqueChannels());
- }
-
- @Test
- public void testGetNumUniqueChildren_multiChannel() throws Exception {
- ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
-
- List<ExpandableNotificationRow> childRows =
- group.getChildrenContainer().getAttachedChildren();
- // Give each child a unique channel id/name.
- int i = 0;
- for (ExpandableNotificationRow childRow : childRows) {
- modifyRanking(childRow.getEntry())
- .setChannel(
- new NotificationChannel(
- "id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT))
- .build();
- i++;
- }
-
- assertEquals(3, group.getNumUniqueChannels());
- }
-
- @Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9e0f83c9fc53..0f1e63f4c0df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -462,7 +462,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -496,7 +495,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -528,7 +526,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f0b4dd46654a..b59385cd5261 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -92,7 +92,6 @@ public class NotificationInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
private static final int TEST_UID = 1;
- private static final int MULTIPLE_CHANNEL_COUNT = 2;
private static final String TEST_CHANNEL = "test_channel";
private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
@@ -100,8 +99,6 @@ public class NotificationInfoTest extends SysuiTestCase {
private NotificationInfo mNotificationInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
- private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
- private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
@@ -162,11 +159,9 @@ public class NotificationInfoTest extends SysuiTestCase {
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
- mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
@@ -185,7 +180,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -212,7 +206,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -235,7 +228,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -267,7 +259,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
entry,
null,
null,
@@ -291,7 +282,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -320,7 +310,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -344,7 +333,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -367,7 +355,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
- mDefaultNotificationChannelSet,
mEntry,
null,
null,
@@ -394,7 +381,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
- mDefaultNotificationChannelSet,
mEntry,
null,
null,
@@ -417,7 +403,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -441,7 +426,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -470,7 +454,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -494,7 +477,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -519,7 +501,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -536,7 +517,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> { },
null,
@@ -551,86 +531,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- (View v, NotificationChannel c, int appUid) -> {
- assertEquals(null, c);
- latch.countDown();
- },
- null,
- mUiEventLogger,
- true,
- true,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
-
- mNotificationInfo.findViewById(R.id.info).performClick();
- // Verify that listener was triggered.
- assertEquals(0, latch.getCount());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
- throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- null,
- null,
- mUiEventLogger,
- true,
- false,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
- final TextView channelNameView =
- mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(GONE, channelNameView.getVisibility());
- }
-
- @Test
- @UiThreadTest
- public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- null,
- null,
- mUiEventLogger,
- true,
- false,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
- assertEquals(GONE, mNotificationInfo.findViewById(
- R.id.interruptiveness_settings).getVisibility());
- assertEquals(VISIBLE, mNotificationInfo.findViewById(
- R.id.non_configurable_multichannel_text).getVisibility());
- }
-
- @Test
public void testBindNotification_whenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
@@ -639,7 +539,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -683,7 +582,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -727,7 +625,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -755,7 +652,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -778,7 +674,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -803,7 +698,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -825,7 +719,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -847,7 +740,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -869,7 +761,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -893,7 +784,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -918,7 +808,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -946,7 +835,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -974,7 +862,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1003,7 +890,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1031,7 +917,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1067,7 +952,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1096,7 +980,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1138,7 +1021,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1176,7 +1058,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1209,7 +1090,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1246,7 +1126,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1285,7 +1164,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1317,7 +1195,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1356,7 +1233,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1386,7 +1262,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1418,7 +1293,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1454,7 +1328,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1488,7 +1361,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1522,7 +1394,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1549,7 +1420,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1562,21 +1432,4 @@ public class NotificationInfoTest extends SysuiTestCase {
assertFalse(mNotificationInfo.willBeRemoved());
}
-
- private Set<NotificationChannel> createMultipleChannelSet(int howMany) {
- Set<NotificationChannel> multiChannelSet = new HashSet<>();
- for (int i = 0; i < howMany; i++) {
- if (i == 0) {
- multiChannelSet.add(mNotificationChannel);
- continue;
- }
-
- NotificationChannel channel = new NotificationChannel(
- TEST_CHANNEL, TEST_CHANNEL_NAME + i, IMPORTANCE_LOW);
-
- multiChannelSet.add(channel);
- }
-
- return multiChannelSet;
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index e42ce2789e0b..ccedd364ef67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -83,8 +83,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
private PartialConversationInfo mInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
- private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
- private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
@@ -144,11 +142,9 @@ public class PartialConversationInfoTest extends SysuiTestCase {
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
- mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setContentTitle(new SpannableString("title"))
.build();
@@ -166,7 +162,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -185,7 +180,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -202,7 +196,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -228,7 +221,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
entry,
null,
true,
@@ -247,7 +239,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -271,7 +262,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -294,7 +284,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -311,7 +300,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -330,7 +318,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
new file mode 100644
index 000000000000..8c3bfd55ecf1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.app.PendingIntent
+import android.app.PendingIntent.CancelListener
+import android.content.Intent
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationTemplateViewWrapper.ActionPendingIntentCancellationHandler
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationTemplateViewWrapperTest : SysuiTestCase() {
+
+ private lateinit var helper: NotificationTestHelper
+
+ private lateinit var root: ViewGroup
+ private lateinit var view: ViewGroup
+ private lateinit var row: ExpandableNotificationRow
+ private lateinit var actions: ViewGroup
+
+ private lateinit var looper: TestableLooper
+
+ @Before
+ fun setUp() {
+ looper = TestableLooper.get(this)
+ allowTestableLooperAsMainThread()
+ helper = NotificationTestHelper(mContext, mDependency, looper)
+ row = helper.createRow()
+ // Some code in the view iterates through parents so we need some extra containers around
+ // it.
+ root = FrameLayout(mContext)
+ val root2 = FrameLayout(mContext)
+ root.addView(root2)
+ view =
+ (LayoutInflater.from(mContext)
+ .inflate(R.layout.notification_template_material_big_text, root2) as ViewGroup)
+ actions = view.findViewById(R.id.actions)!!
+ ViewUtils.attachView(root)
+ }
+
+ @Test
+ fun noActionsPresent_noCrash() {
+ view.removeView(actions)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ wrapper.onContentUpdated(row)
+ }
+
+ @Test
+ fun actionPendingIntentCancelled_actionDisabled() {
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action1 = createActionWithPendingIntent()
+ val action2 = createActionWithPendingIntent()
+ val action3 = createActionWithPendingIntent()
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread() // Wait for cancellation registration to execute.
+
+ val pi3 = getPendingIntent(action3)
+ pi3.cancel()
+ looper.processAllMessages() // Wait for listener callbacks to execute
+
+ assertThat(action1.isEnabled).isTrue()
+ assertThat(action2.isEnabled).isTrue()
+ assertThat(action3.isEnabled).isFalse()
+ assertThat(wrapper.mCancelledPendingIntents)
+ .doesNotContain(getPendingIntent(action1).hashCode())
+ assertThat(wrapper.mCancelledPendingIntents)
+ .doesNotContain(getPendingIntent(action2).hashCode())
+ assertThat(wrapper.mCancelledPendingIntents).contains(pi3.hashCode())
+ }
+
+ @Test
+ fun newActionWithSamePendingIntentPosted_actionDisabled() {
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action = createActionWithPendingIntent()
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread() // Wait for cancellation registration to execute.
+
+ // Cancel the intent and check action is now false.
+ val pi = getPendingIntent(action)
+ pi.cancel()
+ looper.processAllMessages() // Wait for listener callbacks to execute
+ assertThat(action.isEnabled).isFalse()
+
+ // Create a NEW action and make sure that one will also be cancelled with same PI.
+ actions.removeView(action)
+ val newAction = createActionWithPendingIntent(pi)
+ wrapper.onContentUpdated(row)
+ looper.processAllMessages() // Wait for listener callbacks to execute
+
+ assertThat(newAction.isEnabled).isFalse()
+ assertThat(wrapper.mCancelledPendingIntents).containsExactly(pi.hashCode())
+ }
+
+ @Test
+ fun twoActionsWithSameCancelledIntent_bothActionsDisabled() {
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action1 = createActionWithPendingIntent()
+ val action2 = createActionWithPendingIntent()
+ val action3 = createActionWithPendingIntent(getPendingIntent(action2))
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread() // Wait for cancellation registration to execute.
+
+ val pi = getPendingIntent(action2)
+ pi.cancel()
+ looper.processAllMessages() // Wait for listener callbacks to execute
+
+ assertThat(action1.isEnabled).isTrue()
+ assertThat(action2.isEnabled).isFalse()
+ assertThat(action3.isEnabled).isFalse()
+ }
+
+ @Test
+ fun actionPendingIntentCancelled_whileDetached_actionDisabled() {
+ ViewUtils.detachView(root)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ val action = createActionWithPendingIntent()
+ wrapper.onContentUpdated(row)
+ getPendingIntent(action).cancel()
+ ViewUtils.attachView(root)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ assertThat(action.isEnabled).isFalse()
+ }
+
+ @Test
+ fun actionViewDetached_pendingIntentListenersDeregistered() {
+ val pi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_VIEW),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ val spy = Mockito.spy(pi)
+ createActionWithPendingIntent(spy)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ wrapper.onContentUpdated(row)
+ ViewUtils.detachView(root)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ val captor = ArgumentCaptor.forClass(CancelListener::class.java)
+ verify(spy, times(1)).registerCancelListener(captor.capture())
+ verify(spy, times(1)).unregisterCancelListener(captor.value)
+ }
+
+ @Test
+ fun actionViewUpdated_oldPendingIntentListenersRemoved() {
+ val pi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_VIEW),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ val spy = Mockito.spy(pi)
+ val action = createActionWithPendingIntent(spy)
+ val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ // Grab set attach listener
+ val attachListener =
+ Mockito.spy(action.getTag(com.android.systemui.res.R.id.pending_intent_listener_tag))
+ as ActionPendingIntentCancellationHandler
+ action.setTag(com.android.systemui.res.R.id.pending_intent_listener_tag, attachListener)
+
+ // Update pending intent in the existing action
+ val newPi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_ALARM_CHANGED),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ action.setTagInternal(R.id.pending_intent_tag, newPi)
+ wrapper.onContentUpdated(row)
+ waitForUiOffloadThread()
+ looper.processAllMessages()
+
+ // Listeners for original pending intent need to be cleaned up now.
+ val captor = ArgumentCaptor.forClass(CancelListener::class.java)
+ verify(spy, times(1)).registerCancelListener(captor.capture())
+ verify(spy, times(1)).unregisterCancelListener(captor.value)
+ // Attach listener has to be replaced with a new one.
+ assertThat(action.getTag(com.android.systemui.res.R.id.pending_intent_listener_tag))
+ .isNotEqualTo(attachListener)
+ verify(attachListener).remove()
+ }
+
+ private fun createActionWithPendingIntent(): View {
+ val pi =
+ PendingIntent.getActivity(
+ mContext,
+ System.currentTimeMillis().toInt(),
+ Intent(Intent.ACTION_VIEW),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ return createActionWithPendingIntent(pi)
+ }
+
+ private fun createActionWithPendingIntent(pi: PendingIntent): View {
+ val view =
+ LayoutInflater.from(mContext)
+ .inflate(R.layout.notification_material_action, null, false)
+ view.setTagInternal(R.id.pending_intent_tag, pi)
+ actions.addView(view)
+ return view
+ }
+
+ private fun getPendingIntent(action: View): PendingIntent {
+ val pendingIntent = action.getTag(R.id.pending_intent_tag) as PendingIntent
+ assertThat(pendingIntent).isNotNull()
+ return pendingIntent
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5c3dde59596e..0b171b2a23e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -364,7 +364,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
- mUserTracker);
+ mUserTracker,
+ mDeviceProvisionedController);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -1169,7 +1170,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
super(
contentResolver,
powerManager,
@@ -1183,7 +1185,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
flags,
keyguardNotificationVisibilityProvider,
uiEventLogger,
- userTracker
+ userTracker,
+ deviceProvisionedController
);
mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 99e4030e1192..b54fbd3c31b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -90,6 +90,8 @@ class FakeMobileConnectionsRepository(
private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
override val defaultMobileIconGroup = _defaultMobileIconGroup
+ override val isAnySimSecure = MutableStateFlow(false)
+
fun setSubscriptions(subs: List<SubscriptionModel>) {
_subscriptions.value = subs
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index d005972043d7..4d4f33b63f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -135,6 +135,7 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
FakeAirplaneModeRepository(),
wifiRepository,
mock(),
+ mock(),
)
demoRepo =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 6f9764a907fc..9148c7580296 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,6 +37,8 @@ import android.telephony.TelephonyManager
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.R
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
@@ -104,6 +106,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var summaryLogger: TableLogBuffer
@Mock private lateinit var logBufferFactory: TableLogBufferFactory
+ @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
private val mobileMappings = FakeMobileMappingsProxy()
private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
@@ -214,6 +217,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
+ updateMonitor,
)
testScope.runCurrent()
@@ -1048,6 +1052,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
airplaneModeRepository,
wifiRepository,
fullConnectionFactory,
+ updateMonitor
)
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
@@ -1103,7 +1108,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun carrierConfig_initialValueIsFetched() =
testScope.runTest {
-
// Value starts out false
assertThat(underTest.defaultDataSubRatConfig.value.showAtLeast3G).isFalse()
@@ -1151,6 +1155,26 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
assertThat(latest).isEqualTo(null)
}
+ @Test
+ fun anySimSecure_propagatesStateFromKeyguardUpdateMonitor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isAnySimSecure)
+ assertThat(latest).isFalse()
+
+ val updateMonitorCallback = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+ updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+ assertThat(latest).isTrue()
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(false)
+ updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+ assertThat(latest).isFalse()
+ }
+
private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
runCurrent()
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 15b9d61fc96b..c935dbb0ca1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -17,13 +17,14 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
@@ -181,6 +182,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4]))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .doesNotContain(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -192,6 +195,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_tablet))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -203,6 +208,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_laptop))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -214,6 +221,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_watch))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -225,6 +234,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_auto))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -236,6 +247,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -247,6 +260,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -258,6 +273,8 @@ class InternetTileViewModelTest : SysuiTestCase() {
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index a520f6c109cc..49a2648a7cac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiIcons
@@ -136,6 +138,10 @@ class WifiViewModelTest : SysuiTestCase() {
// is used instead
assertThat(latest).isInstanceOf(WifiIcon.Visible::class.java)
assertThat((latest as WifiIcon.Visible).res).isEqualTo(WifiIcons.WIFI_FULL_ICONS[1])
+ assertThat(
+ (latest as WifiIcon.Visible).contentDescription.loadContentDescription(context)
+ )
+ .doesNotContain(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index c8f28bc17926..4ccbd1b739f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -65,7 +65,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
- private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+ private final FakeRotationPolicy mFakeRotationPolicy = new FakeRotationPolicy();
private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
private DeviceStateRotationLockSettingsManager mSettingsManager;
@@ -324,13 +324,21 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
private boolean mRotationLock;
- @Override
public void setRotationLock(boolean enabled) {
- mRotationLock = enabled;
+ setRotationLock(enabled, /* caller= */ "FakeRotationPolicy");
}
@Override
+ public void setRotationLock(boolean enabled, String caller) {
+ mRotationLock = enabled;
+ }
+
public void setRotationLockAtAngle(boolean enabled, int rotation) {
+ setRotationLockAtAngle(enabled, rotation, /* caller= */ "FakeRotationPolicy");
+ }
+
+ @Override
+ public void setRotationLockAtAngle(boolean enabled, int rotation, String caller) {
mRotationLock = enabled;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index bbc49c859821..af941d03f191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -34,7 +34,6 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
@@ -45,6 +44,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -1120,6 +1120,7 @@ class UserInteractorTest : SysuiTestCase() {
),
uiEventLogger = uiEventLogger,
featureFlags = featureFlags,
+ userRestrictionChecker = mock(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 2433e123a309..a8db368d4150 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -267,6 +267,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 8c88f95d73a5..6932f5ed4b30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -45,6 +45,7 @@ import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -176,6 +177,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
),
guestUserInteractor = guestUserInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 8e57dc1f8b2c..daf88773780e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,8 +41,8 @@ import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
import android.content.res.Configuration;
import android.media.AudioManager;
-import android.os.Handler;
import android.os.SystemClock;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Log;
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
+import com.android.systemui.util.settings.FakeSettings;
import org.junit.After;
import org.junit.Before;
@@ -135,6 +136,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
+ private FakeSettings mSecureSettings;
+
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
@@ -147,10 +150,6 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- // Ensure previous tests have not left messages on main looper
- Handler localHandler = new Handler(mTestableLooper.getLooper());
- localHandler.removeCallbacksAndMessages(null);
-
when(mPostureController.getDevicePosture())
.thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
@@ -167,6 +166,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mFeatureFlags = new FakeFeatureFlags();
+ mSecureSettings = new FakeSettings();
+
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -182,7 +183,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags);
+ mFeatureFlags,
+ mSecureSettings);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -247,6 +249,18 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
+ public void testSetTimeoutValue_ComputeTimeout() {
+ mSecureSettings.putInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, 7000);
+ Mockito.reset(mAccessibilityMgr);
+ mDialog.init(0, null);
+ mDialog.rescheduleTimeoutH();
+ verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+ 7000,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
+
+ @Test
public void testComputeTimeout_tooltip() {
Mockito.reset(mAccessibilityMgr);
mDialog.showCaptionsTooltip();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index 94ed608f4844..e59e4759fc7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -15,104 +15,119 @@
*/
package com.android.systemui.wmshell
-import android.content.ContentResolver
import android.content.Context
+import android.content.Intent
import android.content.SharedPreferences
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.core.content.edit
import androidx.test.filters.SmallTest
import com.android.systemui.model.SysUiStateTest
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.PREF_MANAGED_EDUCATION
import com.android.wm.shell.bubbles.PREF_STACK_EDUCATION
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mockito
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class BubbleEducationControllerTest : SysUiStateTest() {
- private val sharedPrefsEditor = Mockito.mock(SharedPreferences.Editor::class.java)
- private val sharedPrefs = Mockito.mock(SharedPreferences::class.java)
- private val context = Mockito.mock(Context::class.java)
+
+ private lateinit var sharedPrefs: SharedPreferences
private lateinit var sut: BubbleEducationController
@Before
fun setUp() {
- Mockito.`when`(context.packageName).thenReturn("packageName")
- Mockito.`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs)
- Mockito.`when`(context.contentResolver)
- .thenReturn(Mockito.mock(ContentResolver::class.java))
- Mockito.`when`(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
- sut = BubbleEducationController(context)
+ sharedPrefs = mContext.getSharedPreferences(mContext.packageName, Context.MODE_PRIVATE)
+ sharedPrefs.edit {
+ remove(PREF_STACK_EDUCATION)
+ remove(PREF_MANAGED_EDUCATION)
+ }
+ sut = BubbleEducationController(mContext)
}
@Test
fun testSeenStackEducation_read() {
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) }
assertEquals(sut.hasSeenStackEducation, true)
- Mockito.verify(sharedPrefs).getBoolean(PREF_STACK_EDUCATION, false)
}
@Test
fun testSeenStackEducation_write() {
sut.hasSeenStackEducation = true
- Mockito.verify(sharedPrefsEditor).putBoolean(PREF_STACK_EDUCATION, true)
+ assertThat(sharedPrefs.getBoolean(PREF_STACK_EDUCATION, false)).isTrue()
}
@Test
fun testSeenManageEducation_read() {
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) }
assertEquals(sut.hasSeenManageEducation, true)
- Mockito.verify(sharedPrefs).getBoolean(PREF_MANAGED_EDUCATION, false)
}
@Test
fun testSeenManageEducation_write() {
sut.hasSeenManageEducation = true
- Mockito.verify(sharedPrefsEditor).putBoolean(PREF_MANAGED_EDUCATION, true)
+ assertThat(sharedPrefs.getBoolean(PREF_MANAGED_EDUCATION, false)).isTrue()
}
@Test
fun testShouldShowStackEducation() {
- val bubble = Mockito.mock(Bubble::class.java)
// When bubble is null
assertEquals(sut.shouldShowStackEducation(null), false)
+ var bubble = createFakeBubble(isConversational = false)
// When bubble is not conversation
- Mockito.`when`(bubble.isConversation).thenReturn(false)
assertEquals(sut.shouldShowStackEducation(bubble), false)
// When bubble is conversation and has seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ bubble = createFakeBubble(isConversational = true)
+ sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) }
assertEquals(sut.shouldShowStackEducation(bubble), false)
// When bubble is conversation and has not seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+ sharedPrefs.edit { remove(PREF_STACK_EDUCATION) }
assertEquals(sut.shouldShowStackEducation(bubble), true)
}
@Test
fun testShouldShowManageEducation() {
- val bubble = Mockito.mock(Bubble::class.java)
// When bubble is null
assertEquals(sut.shouldShowManageEducation(null), false)
+ var bubble = createFakeBubble(isConversational = false)
// When bubble is not conversation
- Mockito.`when`(bubble.isConversation).thenReturn(false)
assertEquals(sut.shouldShowManageEducation(bubble), false)
// When bubble is conversation and has seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ bubble = createFakeBubble(isConversational = true)
+ sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) }
assertEquals(sut.shouldShowManageEducation(bubble), false)
// When bubble is conversation and has not seen stack edu
- Mockito.`when`(bubble.isConversation).thenReturn(true)
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+ sharedPrefs.edit { remove(PREF_MANAGED_EDUCATION) }
assertEquals(sut.shouldShowManageEducation(bubble), true)
}
+
+ private fun createFakeBubble(isConversational: Boolean): Bubble {
+ return if (isConversational) {
+ val shortcutInfo = ShortcutInfo.Builder(mContext, "fakeId").build()
+ Bubble(
+ "key",
+ shortcutInfo,
+ /* desiredHeight= */ 6,
+ Resources.ID_NULL,
+ "title",
+ /* taskId= */ 0,
+ "locus",
+ /* isDismissable= */ true,
+ directExecutor()
+ ) {}
+ } else {
+ val intent = Intent(Intent.ACTION_VIEW).setPackage(mContext.packageName)
+ Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+ }
+ }
}
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 d8511e8f38e2..65b8b555cf2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -126,6 +126,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -391,7 +392,8 @@ public class BubblesTest extends SysuiTestCase {
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
- mock(UserTracker.class)
+ mock(UserTracker.class),
+ mock(DeviceProvisionedController.class)
);
mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 4e14bbf6ac1f..0df235dd2416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.interruption.KeyguardNotifica
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +49,8 @@ public class TestableNotificationInterruptStateProviderImpl
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
super(contentResolver,
powerManager,
ambientDisplayConfiguration,
@@ -61,7 +63,8 @@ public class TestableNotificationInterruptStateProviderImpl
flags,
keyguardNotificationVisibilityProvider,
uiEventLogger,
- userTracker);
+ userTracker,
+ deviceProvisionedController);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 21a5eb7022b0..28557d3e4046 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -17,6 +17,7 @@
package com.android.systemui.broadcast
import android.content.BroadcastReceiver
+import android.content.BroadcastReceiver.PendingResult
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@@ -28,7 +29,6 @@ import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserTracker
-import java.lang.IllegalStateException
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
@@ -96,8 +96,14 @@ class FakeBroadcastDispatcher(
/**
* Sends the given [intent] to *only* the receivers that were registered with an [IntentFilter]
* that matches the intent.
+ *
+ * A non-null [pendingResult] can be used to pass the sending user.
*/
- fun sendIntentToMatchingReceiversOnly(context: Context, intent: Intent) {
+ fun sendIntentToMatchingReceiversOnly(
+ context: Context,
+ intent: Intent,
+ pendingResult: PendingResult? = null
+ ) {
receivers.forEach {
if (
it.filter.match(
@@ -107,6 +113,9 @@ class FakeBroadcastDispatcher(
/* logTag= */ "FakeBroadcastDispatcher",
) > 0
) {
+ if (pendingResult != null) {
+ it.receiver.pendingResult = pendingResult
+ }
it.receiver.onReceive(context, intent)
}
}
@@ -130,4 +139,19 @@ class FakeBroadcastDispatcher(
val receiver: BroadcastReceiver,
val filter: IntentFilter,
)
+
+ companion object {
+ fun fakePendingResultForUser(userId: Int) =
+ PendingResult(
+ /* resultCode = */ 0,
+ /* resultData = */ "",
+ /* resultExtras = */ null,
+ /* type = */ PendingResult.TYPE_REGISTERED,
+ /* ordered = */ false,
+ /* sticky = */ false,
+ /* token = */ null,
+ userId,
+ /* flags = */ 0,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index ceab8e9e52ee..945aaede0087 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -50,6 +50,7 @@ import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.utils.UserRestrictionChecker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.TestScope
import org.mockito.Mockito.mock
@@ -137,6 +138,7 @@ object KeyguardDismissInteractorFactory {
refreshUsersScheduler = mock(RefreshUsersScheduler::class.java),
guestUserInteractor = mock(GuestUserInteractor::class.java),
uiEventLogger = mock(UiEventLogger::class.java),
+ userRestrictionChecker = mock(UserRestrictionChecker::class.java),
)
return WithDependencies(
trustRepository = trustRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
index bf26e719433d..cbf4ae5e3014 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
@@ -21,6 +21,6 @@ import com.android.systemui.plugins.qs.QSTile
class FakeQSFactory(private val tileCreator: (String) -> QSTile?) : QSFactory {
override fun createTile(tileSpec: String): QSTile? {
- return tileCreator(tileSpec)
+ return tileCreator(tileSpec)?.also { it.tileSpec = tileSpec }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
index 9ea079fc9c4b..57ad28289ebd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
@@ -16,15 +16,16 @@
package com.android.systemui.qs.pipeline.data.repository
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
class FakeAutoAddRepository : AutoAddRepository {
private val autoAddedTilesPerUser = mutableMapOf<Int, MutableStateFlow<Set<TileSpec>>>()
- override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
+ override suspend fun autoAddedTiles(userId: Int): StateFlow<Set<TileSpec>> {
return getFlow(userId)
}
@@ -39,4 +40,8 @@ class FakeAutoAddRepository : AutoAddRepository {
private fun getFlow(userId: Int): MutableStateFlow<Set<TileSpec>> =
autoAddedTilesPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) }
+
+ override suspend fun reconcileRestore(restoreData: RestoreData) {
+ with(getFlow(restoreData.userId)) { value = value + restoreData.restoredAutoAddedTiles }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt
new file mode 100644
index 000000000000..e0c2154f2aba
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt
@@ -0,0 +1,16 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeQSSettingsRestoredRepository : QSSettingsRestoredRepository {
+ private val _restoreData = MutableSharedFlow<RestoreData>()
+
+ override val restoreData: Flow<RestoreData>
+ get() = _restoreData
+
+ suspend fun onDataRestored(restoreData: RestoreData) {
+ _restoreData.emit(restoreData)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index aa8dbe120ca4..ae4cf3afe671 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.pipeline.data.repository
+import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.shared.TileSpec
import kotlinx.coroutines.flow.Flow
@@ -26,7 +27,7 @@ class FakeTileSpecRepository : TileSpecRepository {
private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
- override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+ override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
return getFlow(userId).asStateFlow()
}
@@ -57,4 +58,13 @@ class FakeTileSpecRepository : TileSpecRepository {
private fun getFlow(userId: Int): MutableStateFlow<List<TileSpec>> =
tilesPerUser.getOrPut(userId) { MutableStateFlow(emptyList()) }
+
+ override suspend fun reconcileRestore(
+ restoreData: RestoreData,
+ currentAutoAdded: Set<TileSpec>
+ ) {
+ with(getFlow(restoreData.userId)) {
+ value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index 4f9cb35db1a3..be57658a4266 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -46,7 +46,7 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont
}
@Override
- public void setRotationLocked(boolean locked) {
+ public void setRotationLocked(boolean locked, String caller) {
}
@@ -56,7 +56,7 @@ public class FakeRotationLockController extends BaseLeakChecker<RotationLockCont
}
@Override
- public void setRotationLockedAtAngle(boolean locked, int rotation) {
+ public void setRotationLockedAtAngle(boolean locked, int rotation, String caller) {
}
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 0480c22233a6..11189cf9e39c 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -25,5 +25,12 @@ flag {
name: "send_a11y_events_based_on_state"
namespace: "accessibility"
description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
-bug: "295575684"
-} \ No newline at end of file
+ bug: "295575684"
+}
+
+flag {
+ name: "add_window_token_without_lock"
+ namespace: "accessibility"
+ description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock"
+ bug: "297972548"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 05b6eb4a64c1..fa73cffb83ea 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1506,11 +1506,17 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- public void onAdded() {
+ /**
+ * Called when the connection is first created. Add a window token for all known displays.
+ * <p>
+ * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+ * lock because this calls out to WindowManagerService.
+ */
+ void addWindowTokensForAllDisplays() {
final Display[] displays = mDisplayManager.getDisplays();
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
- onDisplayAdded(displayId);
+ addWindowTokenForDisplay(displayId);
}
}
@@ -1518,9 +1524,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
* Called whenever a logical display has been added to the system. Add a window token for adding
* an accessibility overlay.
*
+ * <p>
+ * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+ * lock because this calls out to WindowManagerService.
+ *
* @param displayId The id of the logical display that was added.
*/
- public void onDisplayAdded(int displayId) {
+ void addWindowTokenForDisplay(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 93ba362070d3..60d4ee61fdd4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -154,6 +154,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
+import com.android.internal.util.Preconditions;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -4500,6 +4501,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private int mSystemUiUid = 0;
AccessibilityDisplayListener(Context context, Handler handler) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ // Avoid concerns about one thread adding displays while another thread removes
+ // them by ensuring the looper is the main looper and the DisplayListener
+ // callbacks are always executed on the one main thread.
+ final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
+ final String errorMessage =
+ "AccessibilityDisplayListener must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
+
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -4541,11 +4556,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onDisplayAdded(int displayId) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayAdded must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
final Display display = mDisplayManager.getDisplay(displayId);
if (!isValidDisplay(display)) {
return;
}
+ final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
mDisplaysList.add(display);
mA11yOverlayLayers.put(
@@ -4554,21 +4579,42 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
- if (displayId != Display.DEFAULT_DISPLAY) {
- final List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int i = 0; i < services.size(); i++) {
- AccessibilityServiceConnection boundClient = services.get(i);
- boundClient.onDisplayAdded(displayId);
+ if (Flags.addWindowTokenWithoutLock()) {
+ services = new ArrayList<>(userState.mBoundServices);
+ } else {
+ services = userState.mBoundServices;
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
+ }
}
}
updateMagnificationLocked(userState);
updateWindowsForAccessibilityCallbackLocked(userState);
notifyClearAccessibilityCacheLocked();
}
+ if (Flags.addWindowTokenWithoutLock()) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
+ }
+ }
+ }
}
@Override
public void onDisplayRemoved(int displayId) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayRemoved must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
synchronized (mLock) {
if (!removeDisplayFromList(displayId)) {
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 9e700734d821..7a2a60263d38 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -44,7 +44,6 @@ import android.util.Slog;
import android.view.Display;
import android.view.MotionEvent;
-
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -169,6 +168,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
+ AccessibilityUserState userState = mUserStateWeakReference.get();
+ if (userState != null && Flags.addWindowTokenWithoutLock()) {
+ addWindowTokensForAllDisplays();
+ }
synchronized (mLock) {
if (mService != service) {
if (mService != null) {
@@ -184,7 +187,6 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
}
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
- AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.addServiceLocked(this);
mSystemSupport.onClientChangeLocked(false);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index ab6cc71fecab..693526adc8d6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -224,7 +224,9 @@ class AccessibilityUserState {
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
if (!mBoundServices.contains(serviceConnection)) {
- serviceConnection.onAdded();
+ if (!Flags.addWindowTokenWithoutLock()) {
+ serviceConnection.addWindowTokensForAllDisplays();
+ }
mBoundServices.add(serviceConnection);
mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 208acdfb49e3..53c629a9ed2d 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -25,9 +25,11 @@ import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.Looper;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -35,6 +37,7 @@ import android.view.Display;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.server.utils.Slogf;
import com.android.server.wm.WindowManagerInternal;
@@ -98,46 +101,47 @@ class UiAutomationManager {
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
- synchronized (mLock) {
- if (mUiAutomationService != null) {
- throw new IllegalStateException(
- "UiAutomationService " + mUiAutomationService.mServiceInterface
- + "already registered!");
- }
-
- try {
- owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
- re);
- return;
- }
+ if (mUiAutomationService != null) {
+ throw new IllegalStateException(
+ "UiAutomationService " + mUiAutomationService.mServiceInterface
+ + "already registered!");
+ }
- mUiAutomationFlags = flags;
- mSystemSupport = systemSupport;
- // Ignore registering UiAutomation if it is not allowed to use the accessibility
- // subsystem.
- if (!useAccessibility()) {
- return;
- }
- mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
- mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
- systemActionPerformer, awm);
- mUiAutomationServiceOwner = owner;
- mUiAutomationService.mServiceInterface = serviceClient;
- try {
- mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
- 0);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed registering death link: " + re);
- destroyUiAutomationService();
- return;
- }
+ try {
+ owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
+ re);
+ return;
+ }
- mUiAutomationService.onAdded();
+ mUiAutomationFlags = flags;
+ mSystemSupport = systemSupport;
+ // Ignore registering UiAutomation if it is not allowed to use the accessibility
+ // subsystem.
+ if (!useAccessibility()) {
+ return;
+ }
+ mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
+ mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
+ systemActionPerformer, awm);
+ mUiAutomationServiceOwner = owner;
+ mUiAutomationService.mServiceInterface = serviceClient;
+ try {
+ mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
+ 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed registering death link: " + re);
+ destroyUiAutomationService();
+ return;
+ }
- mUiAutomationService.connectServiceUnknownThread();
+ if (!Flags.addWindowTokenWithoutLock()) {
+ mUiAutomationService.addWindowTokensForAllDisplays();
}
+ // UiAutomationService#connectServiceUnknownThread posts to a handler
+ // so this call should return immediately.
+ mUiAutomationService.connectServiceUnknownThread();
}
void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
@@ -253,6 +257,13 @@ class UiAutomationManager {
super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
securityPolicy, systemSupport, trace, windowManagerInternal,
systemActionPerformer, awm);
+ final boolean isMainHandler = mainHandler.getLooper() == Looper.getMainLooper();
+ final String errorMessage = "UiAutomationService must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
mMainHandler = mainHandler;
setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY);
}
@@ -274,6 +285,9 @@ class UiAutomationManager {
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ mUiAutomationService.addWindowTokensForAllDisplays();
+ }
if (mTrace.isA11yTracingEnabledForTypes(
AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
@@ -286,7 +300,7 @@ class UiAutomationManager {
mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
} catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error initialized connection", re);
+ Slog.w(LOG_TAG, "Error initializing connection", re);
destroyUiAutomationService();
}
});
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ba4533960db4..c79149816b1a 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -155,6 +155,7 @@ public class CompanionDeviceManagerService extends SystemService {
"debug.cdm.cdmservice.removal_time_window";
private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
+ private static final int MAX_CN_LENGTH = 500;
private final ActivityManager mActivityManager;
private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener;
@@ -757,6 +758,9 @@ public class CompanionDeviceManagerService extends SystemService {
String callingPackage = component.getPackageName();
checkCanCallNotificationApi(callingPackage);
// TODO: check userId.
+ if (component.flattenToString().length() > MAX_CN_LENGTH) {
+ throw new IllegalArgumentException("Component name is too long.");
+ }
final long identity = Binder.clearCallingIdentity();
try {
return PendingIntent.getActivityAsUser(getContext(),
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index e8839a2a76ae..720687ef20cc 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -562,7 +562,8 @@ public class SecureChannel {
private byte[] constructToken(D2DHandshakeContext.Role role, byte[] authValue)
throws GeneralSecurityException {
MessageDigest hash = MessageDigest.getInstance("SHA-256");
- byte[] roleUtf8 = role.name().getBytes(StandardCharsets.UTF_8);
+ String roleName = role == Role.INITIATOR ? "Initiator" : "Responder";
+ byte[] roleUtf8 = roleName.getBytes(StandardCharsets.UTF_8);
int tokenLength = roleUtf8.length + authValue.length;
return hash.digest(ByteBuffer.allocate(tokenLength)
.put(roleUtf8)
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index a3ccb168aa4e..9dd0dca47f0e 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -113,13 +113,15 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
private final boolean mCrossTaskNavigationAllowedByDefault;
@NonNull
private final ArraySet<ComponentName> mCrossTaskNavigationExemptions;
+ @Nullable
+ private final ComponentName mPermissionDialogComponent;
private final Object mGenericWindowPolicyControllerLock = new Object();
@Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
private int mDisplayId = Display.INVALID_DISPLAY;
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
- final ArraySet<Integer> mRunningUids = new ArraySet<>();
+ private final ArraySet<Integer> mRunningUids = new ArraySet<>();
@Nullable private final ActivityListener mActivityListener;
@Nullable private final PipBlockedCallback mPipBlockedCallback;
@Nullable private final IntentListenerCallback mIntentListenerCallback;
@@ -171,6 +173,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@NonNull Set<ComponentName> activityPolicyExemptions,
boolean crossTaskNavigationAllowedByDefault,
@NonNull Set<ComponentName> crossTaskNavigationExemptions,
+ @Nullable ComponentName permissionDialogComponent,
@Nullable ActivityListener activityListener,
@Nullable PipBlockedCallback pipBlockedCallback,
@Nullable ActivityBlockedCallback activityBlockedCallback,
@@ -185,6 +188,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
mActivityPolicyExemptions = activityPolicyExemptions;
mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault;
mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions);
+ mPermissionDialogComponent = permissionDialogComponent;
mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
@@ -309,6 +313,13 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
return false;
}
+ // mPermissionDialogComponent being null means we don't want to block permission Dialogs
+ // based on FLAG_STREAM_PERMISSIONS
+ if (mPermissionDialogComponent != null
+ && mPermissionDialogComponent.equals(activityComponent)) {
+ return false;
+ }
+
return true;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 203a152ccc73..a2e4d2cc6929 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -24,6 +24,7 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL
import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -204,6 +205,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@GuardedBy("mVirtualDeviceLock")
@NonNull
private final Set<ComponentName> mActivityPolicyExemptions;
+ private final ComponentName mPermissionDialogComponent;
private ActivityListener createListenerAdapter() {
return new ActivityListener() {
@@ -317,6 +319,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
mParams.getVirtualSensorCallback(), mParams.getVirtualSensorConfigs());
mCameraAccessController = cameraAccessController;
mCameraAccessController.startObservingIfNeeded();
+ if (!Flags.streamPermissions()) {
+ mPermissionDialogComponent = getPermissionDialogComponent();
+ } else {
+ mPermissionDialogComponent = null;
+ }
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -324,8 +331,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
mVirtualDeviceLog.logCreated(deviceId, mOwnerUid);
- mPublicVirtualDeviceObject = new VirtualDevice(
- this, getDeviceId(), getPersistentDeviceId(), mParams.getName());
+ if (Flags.vdmPublicApis()) {
+ mPublicVirtualDeviceObject = new VirtualDevice(
+ this, getDeviceId(), getPersistentDeviceId(), mParams.getName(),
+ getDisplayName());
+ } else {
+ mPublicVirtualDeviceObject = new VirtualDevice(
+ this, getDeviceId(), getPersistentDeviceId(), mParams.getName());
+ }
if (Flags.dynamicPolicy()) {
mActivityPolicyExemptions = new ArraySet<>(
@@ -951,6 +964,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
/*crossTaskNavigationExemptions=*/crossTaskNavigationAllowedByDefault
? mParams.getBlockedCrossTaskNavigations()
: mParams.getAllowedCrossTaskNavigations(),
+ mPermissionDialogComponent,
createListenerAdapter(),
this::onEnteringPipBlocked,
this::onActivityBlocked,
@@ -963,6 +977,13 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
return gwpc;
}
+ private ComponentName getPermissionDialogComponent() {
+ Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+ PackageManager packageManager = mContext.getPackageManager();
+ intent.setPackage(packageManager.getPermissionControllerPackageName());
+ return intent.resolveActivity(packageManager);
+ }
+
int createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
@NonNull IVirtualDisplayCallback callback, String packageName) {
GenericWindowPolicyController gwpc;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d9c269410b93..6521fabe5b7c 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -194,6 +194,7 @@ java_library_static {
"notification_flags_lib",
"camera_platform_flags_core_java_lib",
"biometrics_flags_lib",
+ "am_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 25ca509cb949..8cc2665b3562 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -265,17 +265,17 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
- mStorageManagerService.onUnlockUser(user.getUserIdentifier());
+ mStorageManagerService.onUserUnlocking(user.getUserIdentifier());
}
@Override
public void onUserStopped(@NonNull TargetUser user) {
- mStorageManagerService.onCleanupUser(user.getUserIdentifier());
+ mStorageManagerService.onUserStopped(user.getUserIdentifier());
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
- mStorageManagerService.onStopUser(user.getUserIdentifier());
+ mStorageManagerService.onUserStopping(user.getUserIdentifier());
}
@Override
@@ -1163,8 +1163,8 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private void onUnlockUser(int userId) {
- Slog.d(TAG, "onUnlockUser " + userId);
+ private void onUserUnlocking(int userId) {
+ Slog.d(TAG, "onUserUnlocking " + userId);
if (userId != UserHandle.USER_SYSTEM) {
// Check if this user shares media with another user
@@ -1227,8 +1227,8 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private void onCleanupUser(int userId) {
- Slog.d(TAG, "onCleanupUser " + userId);
+ private void onUserStopped(int userId) {
+ Slog.d(TAG, "onUserStopped " + userId);
try {
mVold.onUserStopped(userId);
@@ -1242,8 +1242,8 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- private void onStopUser(int userId) {
- Slog.i(TAG, "onStopUser " + userId);
+ private void onUserStopping(int userId) {
+ Slog.i(TAG, "onUserStopping " + userId);
try {
mStorageSessionController.onUserStopping(userId);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index c20f0aa4a62a..9716cf69015c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -225,7 +225,7 @@ final class ActivityManagerConstants extends ContentObserver {
/**
* The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
*/
- private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp
new file mode 100644
index 000000000000..af1200e4bdf8
--- /dev/null
+++ b/services/core/java/com/android/server/am/Android.bp
@@ -0,0 +1,10 @@
+aconfig_declarations {
+ name: "am_flags",
+ package: "com.android.server.am",
+ srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "am_flags_lib",
+ aconfig_declarations: "am_flags",
+}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 128bbdf9de9b..907069de8c97 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -82,6 +82,7 @@ import com.android.server.am.AppRestrictionController.TrackerType;
import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider;
import com.android.server.pm.UserManagerInternal;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.Arrays;
@@ -571,8 +572,18 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
builder = new BatteryUsageStatsQuery.Builder()
.includeProcessStateData()
.aggregateSnapshots(lastUidBatteryUsageStartTs, curStart);
- updateBatteryUsageStatsOnceInternal(0, buf, builder, userIds, batteryStatsInternal);
+ final BatteryUsageStats statsCommit =
+ updateBatteryUsageStatsOnceInternal(0,
+ buf,
+ builder,
+ userIds,
+ batteryStatsInternal);
curDuration += curStart - lastUidBatteryUsageStartTs;
+ try {
+ statsCommit.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close a stat");
+ }
}
if (needUpdateUidBatteryUsageInWindow && curDuration >= windowSize) {
// If we do have long enough data for the window, save it.
@@ -648,8 +659,14 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
}
}
}
+ try {
+ stats.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close a stat");
+ }
}
+ // The BatteryUsageStats object MUST BE CLOSED when finished using
private BatteryUsageStats updateBatteryUsageStatsOnceInternal(long expectedDuration,
SparseArray<BatteryUsage> buf, BatteryUsageStatsQuery.Builder builder,
ArraySet<UserHandle> userIds, BatteryStatsInternal batteryStatsInternal) {
@@ -662,7 +679,16 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
// Shouldn't happen unless in test.
return null;
}
+ // We need the first stat in the list, so we should
+ // close out the others.
final BatteryUsageStats stats = statsList.get(0);
+ for (int i = 1; i < statsList.size(); i++) {
+ try {
+ statsList.get(i).close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List");
+ }
+ }
final List<UidBatteryConsumer> uidConsumers = stats.getUidBatteryConsumers();
if (uidConsumers != null) {
final long start = stats.getStatsStartTimestamp();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 5fa0ffaa9606..6005b64ca1bc 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1442,7 +1442,6 @@ public final class CachedAppOptimizer {
uidRec.setFrozen(false);
postUidFrozenMessage(uidRec.getUid(), false);
}
- reportProcessFreezableChangedLocked(app);
opt.setFreezerOverride(false);
if (pid == 0 || !opt.isFrozen()) {
@@ -1481,6 +1480,7 @@ public final class CachedAppOptimizer {
if (processKilled) {
return;
}
+ reportProcessFreezableChangedLocked(app);
long freezeTime = opt.getFreezeUnfreezeTime();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index e84fed7e0a4d..4b622f589adb 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -173,6 +173,16 @@ final class CoreSettingsObserver extends ContentObserver {
TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU,
TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class,
TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
+
+ // Register all text aconfig flags.
+ for (String flag : TextFlags.TEXT_ACONFIGS_FLAGS) {
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
+ TextFlags.NAMESPACE,
+ flag,
+ TextFlags.getKeyForFlag(flag),
+ boolean.class,
+ false)); // All aconfig flags are false by default.
+ }
// add other device configs here...
}
private static volatile boolean sDeviceConfigContextEntriesLoaded = false;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3d11c6843338..0615ecf029fa 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3087,6 +3087,8 @@ public final class ProcessList {
if (old == proc && proc.isPersistent()) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
+ // Ensure that the mCrashing flag is cleared, since this is a restart
+ proc.resetCrashingOnRestart();
} else if (old != null) {
if (old.isKilled()) {
// The old process has been killed, we probably haven't had
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index cfbb5a5bc195..d8a269598bdc 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -618,6 +618,10 @@ class ProcessRecord implements WindowProcessListener {
mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
+ void resetCrashingOnRestart() {
+ mErrorState.setCrashing(false);
+ }
+
@GuardedBy(anyOf = {"mService", "mProcLock"})
UidRecord getUidRecord() {
return mUidRecord;
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
new file mode 100644
index 000000000000..b03cc6295b8d
--- /dev/null
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.am"
+
+flag {
+ name: "oomadjuster_correctness_rewrite"
+ namespace: "android_platform_power_optimization"
+ description: "Utilize new OomAdjuster implementation"
+ bug: "298055811"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index 62c5737a2e8e..c24680e9b06a 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 13ee47eb91c8..40b2f5ab852f 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -43,14 +43,16 @@ class BrightnessRangeController {
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
- new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags);
+ new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags,
+ displayToken, info);
}
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
- HdrClamper hdrClamper, DisplayManagerFlags flags) {
+ HdrClamper hdrClamper, DisplayManagerFlags flags, IBinder displayToken,
+ DisplayDeviceInfo info) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
mHdrClamper = hdrClamper;
@@ -60,10 +62,7 @@ class BrightnessRangeController {
mNormalBrightnessModeController.resetNbmData(
displayDeviceConfig.getLuxThrottlingData());
}
- if (mUseHdrClamper) {
- mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
- }
-
+ updateHdrClamper(info, displayToken, displayDeviceConfig);
}
void dump(PrintWriter pw) {
@@ -101,13 +100,12 @@ class BrightnessRangeController {
displayDeviceConfig::getHdrBrightnessFromSdr);
}
);
- if (mUseHdrClamper) {
- mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
- }
+ updateHdrClamper(info, token, displayDeviceConfig);
}
void stop() {
mHbmController.stop();
+ mHdrClamper.stop();
}
void setAutoBrightnessEnabled(int state) {
@@ -151,6 +149,18 @@ class BrightnessRangeController {
return mHbmController.getTransitionPoint();
}
+ private void updateHdrClamper(DisplayDeviceInfo info, IBinder token,
+ DisplayDeviceConfig displayDeviceConfig) {
+ if (mUseHdrClamper) {
+ DisplayDeviceConfig.HighBrightnessModeData hbmData =
+ displayDeviceConfig.getHighBrightnessModeData();
+ float minimumHdrPercentOfScreen =
+ hbmData == null ? -1f : hbmData.minimumHdrPercentOfScreen;
+ mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData(), info.width,
+ info.height, minimumHdrPercentOfScreen, token);
+ }
+ }
+
private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
if (mUseNbmController) {
boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 8642fb888556..098cb87940b2 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
import android.util.Slog;
@@ -205,6 +206,24 @@ abstract class DisplayDevice {
*/
public Runnable requestDisplayStateLocked(int state, float brightnessState,
float sdrBrightnessState) {
+ return requestDisplayStateLocked(state, brightnessState, sdrBrightnessState, null);
+ }
+
+ /**
+ * Sets the display state, if supported.
+ *
+ * @param state The new display state.
+ * @param brightnessState The new display brightnessState.
+ * @param sdrBrightnessState The new display brightnessState for SDR layers.
+ * @param displayOffloadSession {@link DisplayOffloadSession} associated with current device.
+ * @return A runnable containing work to be deferred until after we have exited the critical
+ * section, or null if none.
+ */
+ public Runnable requestDisplayStateLocked(
+ int state,
+ float brightnessState,
+ float sdrBrightnessState,
+ @Nullable DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
return null;
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 507ae2676b16..9e92c8d7342d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -500,6 +500,8 @@ public class DisplayDeviceConfig {
public static final String DEFAULT_ID = "default";
+ public static final int DEFAULT_LOW_REFRESH_RATE = 60;
+
private static final float BRIGHTNESS_DEFAULT = 0.5f;
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
@@ -513,7 +515,6 @@ public class DisplayDeviceConfig {
private static final int DEFAULT_PEAK_REFRESH_RATE = 0;
private static final int DEFAULT_REFRESH_RATE = 60;
private static final int DEFAULT_REFRESH_RATE_IN_HBM = 0;
- private static final int DEFAULT_LOW_REFRESH_RATE = 60;
private static final int DEFAULT_HIGH_REFRESH_RATE = 0;
private static final float[] DEFAULT_BRIGHTNESS_THRESHOLDS = new float[]{};
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 213ee646fc34..3529b048bd34 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayViewport;
import android.util.DisplayMetrics;
@@ -275,6 +277,11 @@ final class DisplayDeviceInfo {
public int defaultModeId;
/**
+ * The mode of the display which is preferred by user.
+ */
+ public int userPreferredModeId = INVALID_MODE_ID;
+
+ /**
* The supported modes of the display.
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
@@ -472,6 +479,7 @@ final class DisplayDeviceInfo {
|| modeId != other.modeId
|| renderFrameRate != other.renderFrameRate
|| defaultModeId != other.defaultModeId
+ || userPreferredModeId != other.userPreferredModeId
|| !Arrays.equals(supportedModes, other.supportedModes)
|| !Arrays.equals(supportedColorModes, other.supportedColorModes)
|| !Objects.equals(hdrCapabilities, other.hdrCapabilities)
@@ -517,6 +525,7 @@ final class DisplayDeviceInfo {
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
defaultModeId = other.defaultModeId;
+ userPreferredModeId = other.userPreferredModeId;
supportedModes = other.supportedModes;
colorMode = other.colorMode;
supportedColorModes = other.supportedColorModes;
@@ -559,6 +568,7 @@ final class DisplayDeviceInfo {
sb.append(", modeId ").append(modeId);
sb.append(", renderFrameRate ").append(renderFrameRate);
sb.append(", defaultModeId ").append(defaultModeId);
+ sb.append(", userPreferredModeId ").append(userPreferredModeId);
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
sb.append(", colorMode ").append(colorMode);
sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 46ef6c3bd3e4..e942c1711088 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -557,7 +557,7 @@ public final class DisplayManagerService extends SystemService {
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
new FoldSettingProvider(mContext, new SettingsWrapper()), mDisplayDeviceRepo,
new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags);
- mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
+ mDisplayModeDirector = new DisplayModeDirector(context, mHandler, mFlags);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
@@ -1772,7 +1772,7 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(mInjector.getLocalDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayDeviceRepo));
+ mHandler, mDisplayDeviceRepo, mFlags));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
@@ -2028,9 +2028,6 @@ public final class DisplayManagerService extends SystemService {
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
- sendDisplayEventLocked(display, event);
- scheduleTraversalLocked(false);
-
if (mDisplayWindowPolicyControllers.contains(displayId)) {
final IVirtualDevice virtualDevice =
mDisplayWindowPolicyControllers.removeReturnOld(displayId).first;
@@ -2041,6 +2038,9 @@ public final class DisplayManagerService extends SystemService {
});
}
}
+
+ sendDisplayEventLocked(display, event);
+ scheduleTraversalLocked(false);
}
private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
@@ -2093,8 +2093,11 @@ public final class DisplayManagerService extends SystemService {
// Only send a request for display state if display state has already been initialized.
if (state != Display.STATE_UNKNOWN) {
final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
- return device.requestDisplayStateLocked(state, brightnessPair.brightness,
- brightnessPair.sdrBrightness);
+ return device.requestDisplayStateLocked(
+ state,
+ brightnessPair.brightness,
+ brightnessPair.sdrBrightness,
+ display.getDisplayOffloadSessionLocked());
}
}
return null;
@@ -3183,9 +3186,10 @@ public final class DisplayManagerService extends SystemService {
}
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler,
- DisplayAdapter.Listener displayAdapterListener) {
- return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ flags);
}
long getDefaultDisplayDelayTimeout() {
@@ -4806,6 +4810,49 @@ public final class DisplayManagerService extends SystemService {
}
return displayGroupIds;
}
+
+ @Override
+ public DisplayManagerInternal.DisplayOffloadSession registerDisplayOffloader(
+ int displayId, @NonNull DisplayManagerInternal.DisplayOffloader displayOffloader) {
+ if (!mFlags.isDisplayOffloadEnabled()) {
+ return null;
+ }
+ synchronized (mSyncRoot) {
+ LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (logicalDisplay == null) {
+ Slog.w(TAG, "registering DisplayOffloader: LogicalDisplay for displayId="
+ + displayId + " is not found. No Op.");
+ return null;
+ }
+
+ DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(logicalDisplay.getDisplayIdLocked());
+ if (displayPowerController == null) {
+ Slog.w(TAG,
+ "setting doze state override: DisplayPowerController for displayId="
+ + displayId + " is unavailable. No Op.");
+ return null;
+ }
+
+ DisplayOffloadSession session =
+ new DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {
+ synchronized (mSyncRoot) {
+ displayPowerController.overrideDozeScreenState(displayState);
+ }
+ }
+
+ @Override
+ public DisplayOffloader getDisplayOffloader() {
+ return displayOffloader;
+ }
+ };
+ logicalDisplay.setDisplayOffloadSessionLocked(session);
+ displayPowerController.setDisplayOffloadSession(session);
+ return session;
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 83f4df97c5dc..ce98559abe30 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -34,6 +34,8 @@ import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.metrics.LogMaker;
@@ -588,8 +590,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
new SparseArray<>();
private boolean mBootCompleted;
-
private final DisplayManagerFlags mFlags;
+ private int mDozeStateOverride = Display.STATE_UNKNOWN;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
/**
* Creates the display power controller.
@@ -684,7 +687,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
+ mDisplayDevice.getDisplayTokenLocked(),
+ mDisplayDevice.getDisplayDeviceInfoLocked());
mBrightnessThrottler = createBrightnessThrottlerLocked();
@@ -957,6 +962,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
@Override
+ public void overrideDozeScreenState(int displayState) {
+ synchronized (mLock) {
+ if (mDisplayOffloadSession == null ||
+ !DisplayOffloadSession.isSupportedOffloadState(displayState)) {
+ return;
+ }
+ mDozeStateOverride = displayState;
+ sendUpdatePowerState();
+ }
+ }
+
+ @Override
+ public void setDisplayOffloadSession(DisplayOffloadSession session) {
+ mDisplayOffloadSession = session;
+ }
+
+ @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -1518,6 +1540,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
} else {
state = Display.STATE_DOZE;
}
+ state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
if (!mAllowAutoBrightnessWhileDozingConfig) {
brightnessState = mPowerRequest.dozeScreenBrightness;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
@@ -1937,6 +1960,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// We want to scale HDR brightness level with the SDR level, we also need to restore
// SDR brightness immediately when entering dim or low power mode.
animateValue = mBrightnessRangeController.getHdrBrightnessValue();
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
}
final float currentBrightness = mPowerState.getScreenBrightness();
@@ -3001,6 +3025,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mLeadDisplayId=" + mLeadDisplayId);
pw.println(" mLightSensor=" + mLightSensor);
pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
+ pw.println(" mDozeStateOverride=" + mDozeStateOverride);
pw.println();
pw.println("Display Power Controller Locked State:");
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index b0d293a1709f..1652871963b9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -31,6 +31,8 @@ import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.metrics.LogMaker;
@@ -470,9 +472,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
new SparseArray();
private boolean mBootCompleted;
-
private final DisplayManagerFlags mFlags;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
/**
* Creates the display power controller.
*/
@@ -549,7 +552,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mBrightnessThrottler = createBrightnessThrottlerLocked();
mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
+ modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
+ mDisplayDevice.getDisplayTokenLocked(),
+ mDisplayDevice.getDisplayDeviceInfoLocked());
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
@@ -588,21 +593,24 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
if (mDisplayId == Display.DEFAULT_DISPLAY) {
mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
- boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
- @Override
- public void onReduceBrightColorsActivationChanged(boolean activated,
- boolean userInitiated) {
- applyReduceBrightColorsSplineAdjustment();
-
- }
-
- @Override
- public void onReduceBrightColorsStrengthChanged(int strength) {
+ if (mCdsi != null) {
+ boolean active = mCdsi.setReduceBrightColorsListener(
+ new ReduceBrightColorsListener() {
+ @Override
+ public void onReduceBrightColorsActivationChanged(boolean activated,
+ boolean userInitiated) {
+ applyReduceBrightColorsSplineAdjustment();
+
+ }
+
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
applyReduceBrightColorsSplineAdjustment();
}
- });
- if (active) {
- applyReduceBrightColorsSplineAdjustment();
}
} else {
mCdsi = null;
@@ -760,6 +768,24 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
@Override
+ public void overrideDozeScreenState(int displayState) {
+ mHandler.postAtTime(() -> {
+ if (mDisplayOffloadSession == null
+ || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
+ || displayState == Display.STATE_UNKNOWN)) {
+ return;
+ }
+ mDisplayStateController.overrideDozeScreenState(displayState);
+ sendUpdatePowerState();
+ }, mClock.uptimeMillis());
+ }
+
+ @Override
+ public void setDisplayOffloadSession(DisplayOffloadSession session) {
+ mDisplayOffloadSession = session;
+ }
+
+ @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -1540,6 +1566,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
// SDR brightness immediately when entering dim or low power mode.
animateValue = mBrightnessRangeController.getHdrBrightnessValue();
customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
}
final float currentBrightness = mPowerState.getScreenBrightness();
@@ -1871,15 +1898,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private HighBrightnessModeController createHbmControllerLocked(
HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) {
- final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
- final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
- final IBinder displayToken =
- mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
- final String displayUniqueId =
- mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig();
+ final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked();
+ final String displayUniqueId = mDisplayDevice.getUniqueId();
final DisplayDeviceConfig.HighBrightnessModeData hbmData =
ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
- final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked();
return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height,
displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN,
PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) ->
@@ -3025,9 +3049,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
BrightnessRangeController getBrightnessRangeController(
HighBrightnessModeController hbmController, Runnable modeChangeCallback,
DisplayDeviceConfig displayDeviceConfig, Handler handler,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
return new BrightnessRangeController(hbmController,
- modeChangeCallback, displayDeviceConfig, handler, flags);
+ modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info);
}
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index e3108c955a95..181386a93c71 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -139,6 +139,10 @@ public interface DisplayPowerControllerInterface {
boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
boolean waitForNegativeProximity);
+ void overrideDozeScreenState(int displayState);
+
+ void setDisplayOffloadSession(DisplayManagerInternal.DisplayOffloadSession session);
+
/**
* Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
* value.
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 924b1b3c66ab..0a1f316ac059 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,9 @@ import static android.view.Display.Mode.INVALID_MODE_ID;
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -47,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -80,21 +84,24 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private final boolean mIsBootDisplayModeSupported;
+ private final DisplayManagerFlags mFlags;
+
private Context mOverlayContext;
// Called with SyncRoot lock held.
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
- Handler handler, Listener listener) {
- this(syncRoot, context, handler, listener, new Injector());
+ Handler handler, Listener listener, DisplayManagerFlags flags) {
+ this(syncRoot, context, handler, listener, flags, new Injector());
}
@VisibleForTesting
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
- Listener listener, Injector injector) {
+ Listener listener, DisplayManagerFlags flags, Injector injector) {
super(syncRoot, context, handler, listener, TAG);
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
+ mFlags = flags;
}
@Override
@@ -224,6 +231,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private boolean mAllmRequested;
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
+ private boolean mDisplayOffloadActive;
private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
// The supported display modes according to SurfaceFlinger
private SurfaceControl.DisplayMode[] mSfDisplayModes;
@@ -640,6 +648,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.modeId = mActiveModeId;
mInfo.renderFrameRate = mActiveRenderFrameRate;
mInfo.defaultModeId = getPreferredModeId();
+ mInfo.userPreferredModeId = mUserPreferredModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
mInfo.allmSupported = mAllmSupported;
@@ -745,8 +754,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@Override
- public Runnable requestDisplayStateLocked(final int state, final float brightnessState,
- final float sdrBrightnessState) {
+ public Runnable requestDisplayStateLocked(
+ final int state,
+ final float brightnessState,
+ final float sdrBrightnessState,
+ DisplayOffloadSession displayOffloadSession) {
+
// Assume that the brightness is off if the display is being turned off.
assert state != Display.STATE_OFF
|| brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT;
@@ -812,18 +825,39 @@ final class LocalDisplayAdapter extends DisplayAdapter {
+ ", state=" + Display.stateToString(state) + ")");
}
- // We must tell sidekick to stop controlling the display before we
- // can change its power mode, so do that first.
- if (mSidekickActive) {
- Trace.traceBegin(Trace.TRACE_TAG_POWER,
- "SidekickInternal#endDisplayControl");
- try {
- mSidekickInternal.endDisplayControl();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ DisplayOffloader displayOffloader =
+ displayOffloadSession == null
+ ? null
+ : displayOffloadSession.getDisplayOffloader();
+
+ boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+
+ // We must tell sidekick/displayoffload to stop controlling the display
+ // before we can change its power mode, so do that first.
+ if (isDisplayOffloadEnabled) {
+ if (mDisplayOffloadActive && displayOffloader != null) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER,
+ "DisplayOffloader#stopOffload");
+ try {
+ displayOffloader.stopOffload();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ mDisplayOffloadActive = false;
+ }
+ } else {
+ if (mSidekickActive) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER,
+ "SidekickInternal#endDisplayControl");
+ try {
+ mSidekickInternal.endDisplayControl();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ mSidekickActive = false;
}
- mSidekickActive = false;
}
+
final int mode = getPowerModeForState(state);
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
+ "id=" + physicalDisplayId
@@ -835,16 +869,32 @@ final class LocalDisplayAdapter extends DisplayAdapter {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
setCommittedState(state);
+
// If we're entering a suspended (but not OFF) power state and we
- // have a sidekick available, tell it now that it can take control.
- if (Display.isSuspendedState(state) && state != Display.STATE_OFF
- && mSidekickInternal != null && !mSidekickActive) {
- Trace.traceBegin(Trace.TRACE_TAG_POWER,
- "SidekickInternal#startDisplayControl");
- try {
- mSidekickActive = mSidekickInternal.startDisplayControl(state);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ // have a sidekick/displayoffload available, tell it now that it can take
+ // control.
+ if (isDisplayOffloadEnabled) {
+ if (DisplayOffloadSession.isSupportedOffloadState(state) &&
+ displayOffloader != null
+ && !mDisplayOffloadActive) {
+ Trace.traceBegin(
+ Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
+ try {
+ mDisplayOffloadActive = displayOffloader.startOffload();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ }
+ } else {
+ if (Display.isSuspendedState(state) && state != Display.STATE_OFF
+ && mSidekickInternal != null && !mSidekickActive) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER,
+ "SidekickInternal#startDisplayControl");
+ try {
+ mSidekickActive = mSidekickInternal.startDisplayControl(state);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
}
}
}
@@ -857,6 +907,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+
private void setDisplayBrightness(float brightnessState,
float sdrBrightnessState) {
// brightnessState includes invalid, off and full range.
@@ -1344,6 +1395,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
public interface DisplayEventListener {
void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
+ void onHotplugConnectionError(long timestampNanos, int connectionError);
void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
@@ -1366,6 +1418,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@Override
+ public void onHotplugConnectionError(long timestampNanos, int errorCode) {
+ mListener.onHotplugConnectionError(timestampNanos, errorCode);
+ }
+
+ @Override
public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod) {
mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId, renderPeriod);
@@ -1391,6 +1448,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@Override
+ public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+ if (DEBUG) {
+ Slog.d(TAG, "onHotplugConnectionError("
+ + "timestampNanos=" + timestampNanos
+ + ", connectionError=" + connectionError + ")");
+ }
+ }
+
+ @Override
public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0405ebe8b5d2..bd82b81513df 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -137,6 +137,9 @@ final class LogicalDisplay {
private final Rect mTempLayerStackRect = new Rect();
private final Rect mTempDisplayRect = new Rect();
+ /** A session token that controls the offloading operations of this logical display. */
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
/**
* Name of a display group to which the display is assigned.
*/
@@ -470,6 +473,7 @@ final class LogicalDisplay {
mBaseDisplayInfo.modeId = deviceInfo.modeId;
mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate;
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
+ mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
mBaseDisplayInfo.supportedModes = Arrays.copyOf(
deviceInfo.supportedModes, deviceInfo.supportedModes.length);
mBaseDisplayInfo.colorMode = deviceInfo.colorMode;
@@ -940,6 +944,15 @@ final class LogicalDisplay {
return mDisplayGroupName;
}
+ public void setDisplayOffloadSessionLocked(
+ DisplayManagerInternal.DisplayOffloadSession session) {
+ mDisplayOffloadSession = session;
+ }
+
+ public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSessionLocked() {
+ return mDisplayOffloadSession;
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mIsEnabled=" + mIsEnabled);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index a514136e62a2..e46edd9ab07a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -17,9 +17,13 @@
package com.android.server.display.brightness.clamper;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.os.Handler;
+import android.os.IBinder;
import android.os.PowerManager;
+import android.view.SurfaceControlHdrLayerInfoListener;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.config.HdrBrightnessData;
import java.io.PrintWriter;
@@ -33,11 +37,18 @@ public class HdrClamper {
private final Runnable mDebouncer;
+ private final HdrLayerInfoListener mHdrListener;
+
@Nullable
private HdrBrightnessData mHdrBrightnessData = null;
+ @Nullable
+ private IBinder mRegisteredDisplayToken = null;
+
private float mAmbientLux = Float.MAX_VALUE;
+ private boolean mHdrVisible = false;
+
private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
@@ -47,6 +58,12 @@ public class HdrClamper {
public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
Handler handler) {
+ this(clamperChangeListener, handler, new Injector());
+ }
+
+ @VisibleForTesting
+ public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+ Handler handler, Injector injector) {
mClamperChangeListener = clamperChangeListener;
mHandler = handler;
mDebouncer = () -> {
@@ -54,6 +71,10 @@ public class HdrClamper {
mMaxBrightness = mDesiredMaxBrightness;
mClamperChangeListener.onChanged();
};
+ mHdrListener = injector.getHdrListener((visible) -> {
+ mHdrVisible = visible;
+ recalculateBrightnessCap(mHdrBrightnessData, mAmbientLux, mHdrVisible);
+ }, handler);
}
// Called in same looper: mHandler.getLooper()
@@ -72,16 +93,37 @@ public class HdrClamper {
*/
public void onAmbientLuxChange(float ambientLux) {
mAmbientLux = ambientLux;
- recalculateBrightnessCap(mHdrBrightnessData, ambientLux);
+ recalculateBrightnessCap(mHdrBrightnessData, ambientLux, mHdrVisible);
}
/**
* Updates brightness cap config.
* Called in same looper: mHandler.getLooper()
*/
- public void resetHdrConfig(HdrBrightnessData data) {
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public void resetHdrConfig(HdrBrightnessData data, int width, int height,
+ float minimumHdrPercentOfScreen, IBinder displayToken) {
mHdrBrightnessData = data;
- recalculateBrightnessCap(data, mAmbientLux);
+ mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
+ if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
+ if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
+ mHdrListener.unregister(mRegisteredDisplayToken);
+ mHdrVisible = false;
+ }
+ if (displayToken != null) { // new token not null, subscribe
+ mHdrListener.register(displayToken);
+ }
+ mRegisteredDisplayToken = displayToken;
+ }
+ recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
+ }
+
+ /** Clean up all resources */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ public void stop() {
+ if (mRegisteredDisplayToken != null) {
+ mHdrListener.unregister(mRegisteredDisplayToken);
+ }
}
/**
@@ -98,13 +140,28 @@ public class HdrClamper {
pw.println(" mAmbientLux=" + mAmbientLux);
}
- private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux) {
- if (data == null) {
- mHandler.removeCallbacks(mDebouncer);
+ private void reset() {
+ if (mMaxBrightness == PowerManager.BRIGHTNESS_MAX
+ && mDesiredMaxBrightness == PowerManager.BRIGHTNESS_MAX && mTransitionRate == -1f
+ && mDesiredTransitionRate == -1f) { // already done reset, do nothing
return;
}
- float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
+ mHandler.removeCallbacks(mDebouncer);
+ mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+ mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+ mDesiredTransitionRate = -1f;
+ mTransitionRate = 1f;
+ mClamperChangeListener.onChanged();
+ }
+ private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux,
+ boolean hdrVisible) {
+ if (data == null || !hdrVisible) {
+ reset();
+ return;
+ }
+
+ float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
if (mMaxBrightness == expectedMaxBrightness) {
mDesiredMaxBrightness = mMaxBrightness;
mDesiredTransitionRate = -1f;
@@ -127,6 +184,8 @@ public class HdrClamper {
mHandler.removeCallbacks(mDebouncer);
mHandler.postDelayed(mDebouncer, debounceTime);
}
+ // do nothing if expectedMaxBrightness == mDesiredMaxBrightness
+ // && expectedMaxBrightness != mMaxBrightness
}
private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
@@ -143,4 +202,36 @@ public class HdrClamper {
}
return foundMaxBrightness;
}
+
+ @FunctionalInterface
+ interface HdrListener {
+ void onHdrVisible(boolean visible);
+ }
+
+ static class HdrLayerInfoListener extends SurfaceControlHdrLayerInfoListener {
+ private final HdrListener mHdrListener;
+
+ private final Handler mHandler;
+
+ private float mHdrMinPixels = Float.MAX_VALUE;
+
+ HdrLayerInfoListener(HdrListener hdrListener, Handler handler) {
+ mHdrListener = hdrListener;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, int maxW,
+ int maxH, int flags, float maxDesiredHdrSdrRatio) {
+ mHandler.post(() ->
+ mHdrListener.onHdrVisible(
+ numberOfHdrLayers > 0 && (float) (maxW * maxH) >= mHdrMinPixels));
+ }
+ }
+
+ static class Injector {
+ HdrLayerInfoListener getHdrListener(HdrListener hdrListener, Handler handler) {
+ return new HdrLayerInfoListener(hdrListener, handler);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 3f6bf1adfe48..b6273e1daf82 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -47,6 +47,26 @@ public class DisplayManagerFlags {
Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
Flags::enableAdaptiveToneImprovements1);
+ private final FlagState mDisplayOffloadFlagState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAY_OFFLOAD,
+ Flags::enableDisplayOffload);
+
+ private final FlagState mDisplayResolutionRangeVotingState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING,
+ Flags::enableDisplayResolutionRangeVoting);
+
+ private final FlagState mUserPreferredModeVoteState = new FlagState(
+ Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE,
+ Flags::enableUserPreferredModeVote);
+
+ private final FlagState mExternalDisplayLimitModeState = new FlagState(
+ Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
+ Flags::enableModeLimitForExternalDisplay);
+
+ private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION,
+ Flags::enableDisplaysRefreshRatesSynchronization);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -68,6 +88,38 @@ public class DisplayManagerFlags {
return mAdaptiveToneImprovements1.isEnabled();
}
+ /** Returns whether resolution range voting feature is enabled or not. */
+ public boolean isDisplayResolutionRangeVotingEnabled() {
+ return mDisplayResolutionRangeVotingState.isEnabled();
+ }
+
+ /**
+ * @return Whether user preferred mode is added as a vote in
+ * {@link com.android.server.display.mode.DisplayModeDirector}
+ */
+ public boolean isUserPreferredModeVoteEnabled() {
+ return mUserPreferredModeVoteState.isEnabled();
+ }
+
+ /**
+ * @return Whether external display mode limitation is enabled.
+ */
+ public boolean isExternalDisplayLimitModeEnabled() {
+ return mExternalDisplayLimitModeState.isEnabled();
+ }
+
+ /**
+ * @return Whether displays refresh rate synchronization is enabled.
+ */
+ public boolean isDisplaysRefreshRatesSynchronizationEnabled() {
+ return mDisplaysRefreshRatesSynchronizationState.isEnabled();
+ }
+
+ /** Returns whether displayoffload is enabled on not */
+ public boolean isDisplayOffloadEnabled() {
+ return mDisplayOffloadFlagState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 4d8600448c33..542f26cbec69 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -5,7 +5,7 @@ package: "com.android.server.display.feature.flags"
flag {
name: "enable_connected_display_management"
namespace: "display_manager"
- description: "Feature flag for Connected Display managment"
+ description: "Feature flag for Connected Display management"
bug: "280739508"
is_fixed_read_only: true
}
@@ -34,3 +34,41 @@ flag {
is_fixed_read_only: true
}
+flag {
+ name: "enable_display_resolution_range_voting"
+ namespace: "display_manager"
+ description: "Feature flag to enable voting for ranges of resolutions"
+ bug: "299297058"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_user_preferred_mode_vote"
+ namespace: "display_manager"
+ description: "Feature flag to use voting for UserPreferredMode for display"
+ bug: "297018612"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_mode_limit_for_external_display"
+ namespace: "display_manager"
+ description: "Feature limiting external display resolution and refresh rate"
+ bug: "242093547"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_displays_refresh_rates_synchronization"
+ namespace: "display_manager"
+ description: "Enables synchronization of refresh rates across displays"
+ bug: "294015845"
+}
+
+flag {
+ name: "enable_display_offload"
+ namespace: "display_manager"
+ description: "Feature flag for DisplayOffload"
+ bug: "299521647"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 2c2af3f7f435..71ea8cc30405 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -19,6 +19,9 @@ package com.android.server.display.mode;
import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
+import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -71,6 +74,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.DeviceConfigParsingUtils;
@@ -84,9 +88,11 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.function.IntSupplier;
@@ -96,6 +102,8 @@ import java.util.function.IntSupplier;
* picked by the system based on system-wide and display-specific configuration.
*/
public class DisplayModeDirector {
+ public static final float SYNCHRONIZED_REFRESH_RATE_TARGET = DEFAULT_LOW_REFRESH_RATE;
+ public static final float SYNCHRONIZED_REFRESH_RATE_TOLERANCE = 1;
private static final String TAG = "DisplayModeDirector";
private boolean mLoggingEnabled;
@@ -151,12 +159,38 @@ public class DisplayModeDirector {
@DisplayManager.SwitchingType
private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
- public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
- this(context, handler, new RealInjector(context));
+ /**
+ * Whether resolution range voting feature is enabled.
+ */
+ private final boolean mIsDisplayResolutionRangeVotingEnabled;
+
+ /**
+ * Whether user preferred mode voting feature is enabled.
+ */
+ private final boolean mIsUserPreferredModeVoteEnabled;
+
+ /**
+ * Whether limit display mode feature is enabled.
+ */
+ private final boolean mIsExternalDisplayLimitModeEnabled;
+
+ private final boolean mIsDisplaysRefreshRatesSynchronizationEnabled;
+
+ public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
+ @NonNull DisplayManagerFlags displayManagerFlags) {
+ this(context, handler, new RealInjector(context), displayManagerFlags);
}
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
- @NonNull Injector injector) {
+ @NonNull Injector injector,
+ @NonNull DisplayManagerFlags displayManagerFlags) {
+ mIsDisplayResolutionRangeVotingEnabled = displayManagerFlags
+ .isDisplayResolutionRangeVotingEnabled();
+ mIsUserPreferredModeVoteEnabled = displayManagerFlags.isUserPreferredModeVoteEnabled();
+ mIsExternalDisplayLimitModeEnabled = displayManagerFlags
+ .isExternalDisplayLimitModeEnabled();
+ mIsDisplaysRefreshRatesSynchronizationEnabled = displayManagerFlags
+ .isDisplaysRefreshRatesSynchronizationEnabled();
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
@@ -230,6 +264,8 @@ public class DisplayModeDirector {
public float maxRenderFrameRate;
public int width;
public int height;
+ public int minWidth;
+ public int minHeight;
public boolean disableRefreshRateSwitching;
public float appRequestBaseModeRefreshRate;
@@ -244,6 +280,8 @@ public class DisplayModeDirector {
maxRenderFrameRate = Float.POSITIVE_INFINITY;
width = Vote.INVALID_SIZE;
height = Vote.INVALID_SIZE;
+ minWidth = 0;
+ minHeight = 0;
disableRefreshRateSwitching = false;
appRequestBaseModeRefreshRate = 0f;
}
@@ -256,6 +294,8 @@ public class DisplayModeDirector {
+ ", maxRenderFrameRate=" + maxRenderFrameRate
+ ", width=" + width
+ ", height=" + height
+ + ", minWidth=" + minWidth
+ + ", minHeight=" + minHeight
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate;
}
@@ -277,7 +317,6 @@ public class DisplayModeDirector {
continue;
}
-
// For physical refresh rates, just use the tightest bounds of all the votes.
// The refresh rate cannot be lower than the minimal render frame rate.
final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min,
@@ -298,10 +337,18 @@ public class DisplayModeDirector {
// For display size, disable refresh rate switching and base mode refresh rate use only
// the first vote we come across (i.e. the highest priority vote that includes the
// attribute).
- if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
- && vote.height > 0 && vote.width > 0) {
- summary.width = vote.width;
- summary.height = vote.height;
+ if (vote.height > 0 && vote.width > 0) {
+ if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) {
+ summary.width = vote.width;
+ summary.height = vote.height;
+ summary.minWidth = vote.minWidth;
+ summary.minHeight = vote.minHeight;
+ } else if (mIsDisplayResolutionRangeVotingEnabled) {
+ summary.width = Math.min(summary.width, vote.width);
+ summary.height = Math.min(summary.height, vote.height);
+ summary.minWidth = Math.max(summary.minWidth, vote.minWidth);
+ summary.minHeight = Math.max(summary.minHeight, vote.minHeight);
+ }
}
if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
summary.disableRefreshRateSwitching = true;
@@ -413,6 +460,8 @@ public class DisplayModeDirector {
|| primarySummary.width == Vote.INVALID_SIZE) {
primarySummary.width = defaultMode.getPhysicalWidth();
primarySummary.height = defaultMode.getPhysicalHeight();
+ } else if (mIsDisplayResolutionRangeVotingEnabled) {
+ updateSummaryWithBestAllowedResolution(modes, primarySummary);
}
availableModes = filterModes(modes, primarySummary);
@@ -654,6 +703,38 @@ public class DisplayModeDirector {
return availableModes;
}
+ private void updateSummaryWithBestAllowedResolution(final Display.Mode[] supportedModes,
+ VoteSummary outSummary) {
+ final int maxAllowedWidth = outSummary.width;
+ final int maxAllowedHeight = outSummary.height;
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "updateSummaryWithBestAllowedResolution " + outSummary);
+ }
+ outSummary.width = Vote.INVALID_SIZE;
+ outSummary.height = Vote.INVALID_SIZE;
+
+ int maxNumberOfPixels = 0;
+ for (Display.Mode mode : supportedModes) {
+ if (mode.getPhysicalWidth() > maxAllowedWidth
+ || mode.getPhysicalHeight() > maxAllowedHeight
+ || mode.getPhysicalWidth() < outSummary.minWidth
+ || mode.getPhysicalHeight() < outSummary.minHeight) {
+ continue;
+ }
+
+ int numberOfPixels = mode.getPhysicalHeight() * mode.getPhysicalWidth();
+ if (numberOfPixels > maxNumberOfPixels || (mode.getPhysicalWidth() == maxAllowedWidth
+ && mode.getPhysicalHeight() == maxAllowedHeight)) {
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "updateSummaryWithBestAllowedResolution updated with " + mode);
+ }
+ maxNumberOfPixels = numberOfPixels;
+ outSummary.width = mode.getPhysicalWidth();
+ outSummary.height = mode.getPhysicalHeight();
+ }
+ }
+ }
+
/**
* Gets the observer responsible for application display mode requests.
*/
@@ -1393,11 +1474,38 @@ public class DisplayModeDirector {
private final Context mContext;
private final Handler mHandler;
private final VotesStorage mVotesStorage;
+ private int mExternalDisplayPeakWidth;
+ private int mExternalDisplayPeakHeight;
+ private int mExternalDisplayPeakRefreshRate;
+ private final boolean mRefreshRateSynchronizationEnabled;
+ private final Set<Integer> mExternalDisplaysConnected = new HashSet<>();
DisplayObserver(Context context, Handler handler, VotesStorage votesStorage) {
mContext = context;
mHandler = handler;
mVotesStorage = votesStorage;
+ mExternalDisplayPeakRefreshRate = mContext.getResources().getInteger(
+ R.integer.config_externalDisplayPeakRefreshRate);
+ mExternalDisplayPeakWidth = mContext.getResources().getInteger(
+ R.integer.config_externalDisplayPeakWidth);
+ mExternalDisplayPeakHeight = mContext.getResources().getInteger(
+ R.integer.config_externalDisplayPeakHeight);
+ mRefreshRateSynchronizationEnabled = mContext.getResources().getBoolean(
+ R.bool.config_refreshRateSynchronizationEnabled);
+ }
+
+ private boolean isExternalDisplayLimitModeEnabled() {
+ return mExternalDisplayPeakWidth > 0
+ && mExternalDisplayPeakHeight > 0
+ && mExternalDisplayPeakRefreshRate > 0
+ && mIsExternalDisplayLimitModeEnabled
+ && mIsDisplayResolutionRangeVotingEnabled
+ && mIsUserPreferredModeVoteEnabled;
+ }
+
+ private boolean isRefreshRateSynchronizationEnabled() {
+ return mRefreshRateSynchronizationEnabled
+ && mIsDisplaysRefreshRatesSynchronizationEnabled;
}
public void observe() {
@@ -1428,6 +1536,9 @@ public class DisplayModeDirector {
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
+ updateUserSettingDisplayPreferredSize(displayInfo);
+ updateDisplaysPeakRefreshRateAndResolution(displayInfo);
+ addDisplaysSynchronizedPeakRefreshRate(displayInfo);
}
@Override
@@ -1437,6 +1548,9 @@ public class DisplayModeDirector {
mDefaultModeByDisplay.remove(displayId);
}
updateLayoutLimitedFrameRate(displayId, null);
+ removeUserSettingDisplayPreferredSize(displayId);
+ removeDisplaysPeakRefreshRateAndResolution(displayId);
+ removeDisplaysSynchronizedPeakRefreshRate(displayId);
}
@Override
@@ -1444,6 +1558,7 @@ public class DisplayModeDirector {
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
+ updateUserSettingDisplayPreferredSize(displayInfo);
}
@Nullable
@@ -1460,6 +1575,111 @@ public class DisplayModeDirector {
mVotesStorage.updateVote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote);
}
+ private void removeUserSettingDisplayPreferredSize(int displayId) {
+ if (!mIsUserPreferredModeVoteEnabled) {
+ return;
+ }
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+ null);
+ }
+
+ private void updateUserSettingDisplayPreferredSize(@Nullable DisplayInfo info) {
+ if (info == null || !mIsUserPreferredModeVoteEnabled) {
+ return;
+ }
+
+ var preferredMode = findDisplayPreferredMode(info);
+ if (preferredMode == null) {
+ removeUserSettingDisplayPreferredSize(info.displayId);
+ return;
+ }
+
+ mVotesStorage.updateVote(info.displayId,
+ Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+ Vote.forSize(/* width */ preferredMode.getPhysicalWidth(),
+ /* height */ preferredMode.getPhysicalHeight()));
+ }
+
+ @Nullable
+ private Display.Mode findDisplayPreferredMode(@NonNull DisplayInfo info) {
+ if (info.userPreferredModeId == INVALID_MODE_ID) {
+ return null;
+ }
+ for (var mode : info.supportedModes) {
+ if (mode.getModeId() == info.userPreferredModeId) {
+ return mode;
+ }
+ }
+ return null;
+ }
+
+ private void removeDisplaysPeakRefreshRateAndResolution(int displayId) {
+ if (!isExternalDisplayLimitModeEnabled()) {
+ return;
+ }
+
+ mVotesStorage.updateVote(displayId,
+ Vote.PRIORITY_LIMIT_MODE, null);
+ }
+
+ private void updateDisplaysPeakRefreshRateAndResolution(@Nullable final DisplayInfo info) {
+ // Only consider external display, only in case the refresh rate and resolution limits
+ // are non-zero.
+ if (info == null || info.type != Display.TYPE_EXTERNAL
+ || !isExternalDisplayLimitModeEnabled()) {
+ return;
+ }
+
+ mVotesStorage.updateVote(info.displayId,
+ Vote.PRIORITY_LIMIT_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ /* minWidth */ 0, /* minHeight */ 0,
+ mExternalDisplayPeakWidth,
+ mExternalDisplayPeakHeight,
+ /* minPhysicalRefreshRate */ 0,
+ mExternalDisplayPeakRefreshRate));
+ }
+
+ /**
+ * Sets 60Hz target refresh rate as the vote with
+ * {@link Vote#PRIORITY_SYNCHRONIZED_REFRESH_RATE} priority.
+ */
+ private void addDisplaysSynchronizedPeakRefreshRate(@Nullable final DisplayInfo info) {
+ if (info == null || info.type != Display.TYPE_EXTERNAL
+ || !isRefreshRateSynchronizationEnabled()) {
+ return;
+ }
+ synchronized (mLock) {
+ mExternalDisplaysConnected.add(info.displayId);
+ if (mExternalDisplaysConnected.size() != 1) {
+ return;
+ }
+ }
+ // set minRefreshRate as the max refresh rate.
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE,
+ Vote.forPhysicalRefreshRates(
+ SYNCHRONIZED_REFRESH_RATE_TARGET
+ - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+ SYNCHRONIZED_REFRESH_RATE_TARGET
+ + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+ }
+
+ private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) {
+ if (!isRefreshRateSynchronizationEnabled()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mExternalDisplaysConnected.contains(displayId)) {
+ return;
+ }
+ mExternalDisplaysConnected.remove(displayId);
+ if (mExternalDisplaysConnected.size() != 0) {
+ return;
+ }
+ }
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
+ }
+
private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) {
if (info == null) {
return;
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index a42d8f257ddf..b6a6069b5a63 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -18,6 +18,8 @@ package com.android.server.display.mode;
import android.view.SurfaceControl;
+import java.util.Objects;
+
final class Vote {
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
// priority vote, it's overridden by all other considerations. It acts to set a default
@@ -36,12 +38,15 @@ final class Vote {
// It votes [minRefreshRate, Float.POSITIVE_INFINITY]
static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
+ // User setting preferred display resolution.
+ static final int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
+
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
// frame rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
- static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;
+ static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -67,40 +72,47 @@ final class Vote {
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
- static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
- static final int PRIORITY_APP_REQUEST_SIZE = 6;
+ static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6;
+
+ static final int PRIORITY_APP_REQUEST_SIZE = 7;
// SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
// rest of low priority voters. It votes [0, max(PEAK, MIN)]
- static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;
+ static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8;
+
+ // Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz].
+ static final int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9;
+
+ // Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
+ static final int PRIORITY_LIMIT_MODE = 10;
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
- static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;
+ static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11;
// For concurrent displays we want to limit refresh rate on all displays
- static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9;
+ static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
// LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- static final int PRIORITY_LOW_POWER_MODE = 10;
+ static final int PRIORITY_LOW_POWER_MODE = 13;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 11;
+ static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- static final int PRIORITY_SKIN_TEMPERATURE = 12;
+ static final int PRIORITY_SKIN_TEMPERATURE = 15;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- static final int PRIORITY_PROXIMITY = 13;
+ static final int PRIORITY_PROXIMITY = 16;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- static final int PRIORITY_UDFPS = 14;
+ static final int PRIORITY_UDFPS = 17;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -127,6 +139,14 @@ final class Vote {
*/
public final int height;
/**
+ * Min requested width of the display in pixels, or 0;
+ */
+ public final int minWidth;
+ /**
+ * Min requested height of the display in pixels, or 0;
+ */
+ public final int minHeight;
+ /**
* Information about the refresh rate frame rate ranges DM would like to set the display to.
*/
public final SurfaceControl.RefreshRateRanges refreshRateRanges;
@@ -144,42 +164,82 @@ final class Vote {
public final float appRequestBaseModeRefreshRate;
static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
- Float.POSITIVE_INFINITY,
- minRefreshRate == maxRefreshRate, 0f);
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ minRefreshRate,
+ /* maxPhysicalRefreshRate= */ maxRefreshRate,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ minFrameRate,
maxFrameRate,
- false, 0f);
+ /* disableRefreshRateSwitching= */ false,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forSize(int width, int height) {
- return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
- false,
- 0f);
+ return new Vote(/* minWidth= */ width, /* minHeight= */ height,
+ width, height,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ false,
+ /* baseModeRefreshRate= */ 0f);
+ }
+
+ static Vote forSizeAndPhysicalRefreshRatesRange(int minWidth, int minHeight,
+ int width, int height, float minRefreshRate, float maxRefreshRate) {
+ return new Vote(minWidth, minHeight,
+ width, height,
+ minRefreshRate,
+ maxRefreshRate,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forDisableRefreshRateSwitching() {
- return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
- Float.POSITIVE_INFINITY, true,
- 0f);
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ true,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
- Float.POSITIVE_INFINITY, false,
- baseModeRefreshRate);
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ false,
+ /* baseModeRefreshRate= */ baseModeRefreshRate);
}
- private Vote(int width, int height,
+ private Vote(int minWidth, int minHeight,
+ int width, int height,
float minPhysicalRefreshRate,
float maxPhysicalRefreshRate,
float minRenderFrameRate,
float maxRenderFrameRate,
boolean disableRefreshRateSwitching,
float baseModeRefreshRate) {
+ this.minWidth = minWidth;
+ this.minHeight = minHeight;
this.width = width;
this.height = height;
this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
@@ -215,6 +275,12 @@ final class Vote {
return "PRIORITY_UDFPS";
case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE";
+ case PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE:
+ return "PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE";
+ case PRIORITY_LIMIT_MODE:
+ return "PRIORITY_LIMIT_MODE";
+ case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
+ return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
@@ -229,9 +295,29 @@ final class Vote {
@Override
public String toString() {
return "Vote: {"
- + "width: " + width + ", height: " + height
+ + "minWidth: " + minWidth + ", minHeight: " + minHeight
+ + ", width: " + width + ", height: " + height
+ ", refreshRateRanges: " + refreshRateRanges
+ ", disableRefreshRateSwitching: " + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate: " + appRequestBaseModeRefreshRate + "}";
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(minWidth, minHeight, width, height, refreshRateRanges,
+ disableRefreshRateSwitching, appRequestBaseModeRefreshRate);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Vote)) return false;
+ final var vote = (Vote) o;
+ return minWidth == vote.minWidth && minHeight == vote.minHeight
+ && width == vote.width && height == vote.height
+ && disableRefreshRateSwitching == vote.disableRefreshRateSwitching
+ && Float.compare(vote.appRequestBaseModeRefreshRate,
+ appRequestBaseModeRefreshRate) == 0
+ && refreshRateRanges.equals(vote.refreshRateRanges);
+ }
}
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index bdd2ab7d63b4..49c587aa5596 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -31,7 +31,8 @@ class VotesStorage {
private static final String TAG = "VotesStorage";
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
- private static final int GLOBAL_ID = -1;
+ @VisibleForTesting
+ static final int GLOBAL_ID = -1;
private boolean mLoggingEnabled;
@@ -91,6 +92,7 @@ class VotesStorage {
+ ", vote=" + vote);
return;
}
+ boolean changed = false;
SparseArray<Vote> votes;
synchronized (mStorageLock) {
if (mVotesByDisplay.contains(displayId)) {
@@ -99,10 +101,13 @@ class VotesStorage {
votes = new SparseArray<>();
mVotesByDisplay.put(displayId, votes);
}
- if (vote != null) {
+ var currentVote = votes.get(priority);
+ if (vote != null && !vote.equals(currentVote)) {
votes.put(priority, vote);
- } else {
+ changed = true;
+ } else if (vote == null && currentVote != null) {
votes.remove(priority);
+ changed = true;
}
}
Trace.traceCounter(Trace.TRACE_TAG_POWER,
@@ -111,7 +116,9 @@ class VotesStorage {
if (mLoggingEnabled) {
Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
}
- mListener.onChanged();
+ if (changed) {
+ mListener.onChanged();
+ }
}
/** dump class values, for debugging */
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index b1a1c601cc0c..5d6e65071479 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -32,6 +32,7 @@ import java.io.PrintWriter;
public class DisplayStateController {
private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
private boolean mPerformScreenOffTransition = false;
+ private int mDozeStateOverride = Display.STATE_UNKNOWN;
public DisplayStateController(DisplayPowerProximityStateController
displayPowerProximityStateController) {
@@ -65,6 +66,7 @@ public class DisplayStateController {
} else {
state = Display.STATE_DOZE;
}
+ state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
break;
case DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM:
case DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT:
@@ -84,6 +86,10 @@ public class DisplayStateController {
return state;
}
+ public void overrideDozeScreenState(int displayState) {
+ mDozeStateOverride = displayState;
+ }
+
/**
* Checks if the screen off transition is to be performed or not.
*/
@@ -100,6 +106,8 @@ public class DisplayStateController {
pw.println();
pw.println("DisplayStateController:");
pw.println(" mPerformScreenOffTransition:" + mPerformScreenOffTransition);
+ pw.println(" mDozeStateOverride=" + mDozeStateOverride);
+
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
if (mDisplayPowerProximityStateController != null) {
mDisplayPowerProximityStateController.dumpLocal(ipw);
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 41053e908a00..68848a2ad426 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -16,35 +16,65 @@
package com.android.server.grammaticalinflection;
+import static android.app.Flags.systemTermsOfAddressEnabled;
import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
import android.annotation.Nullable;
+import android.app.GrammaticalInflectionManager;
import android.app.IGrammaticalInflectionManager;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.IBinder;
+import android.os.Environment;
import android.os.Process;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.util.AtomicFile;
import android.util.Log;
+import android.util.SparseIntArray;
+import android.util.Xml;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
/**
* The implementation of IGrammaticalInflectionManager.aidl.
*
* <p>This service is API entry point for storing app-specific grammatical inflection.
*/
public class GrammaticalInflectionService extends SystemService {
- private final String TAG = "GrammaticalInflection";
+ private static final String TAG = "GrammaticalInflection";
+ private static final String ATTR_NAME = "grammatical_gender";
+ private static final String USER_SETTINGS_FILE_NAME = "user_settings.xml";
+ private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection";
+ private static final String GRAMMATICAL_INFLECTION_ENABLED =
+ "i18n.grammatical_Inflection.enabled";
+
private final GrammaticalInflectionBackupHelper mBackupHelper;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private final Object mLock = new Object();
+ private final SparseIntArray mGrammaticalGenderCache = new SparseIntArray();
+
private PackageManagerInternal mPackageManagerInternal;
- private static final String GRAMMATICAL_INFLECTION_ENABLED =
- "i18n.grammatical_Inflection.enabled";
+ private GrammaticalInflectionService.GrammaticalInflectionBinderService mBinderService;
/**
* Initializes the system service.
@@ -62,22 +92,46 @@ public class GrammaticalInflectionService extends SystemService {
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mBackupHelper = new GrammaticalInflectionBackupHelper(
this, context.getPackageManager());
+ mBinderService = new GrammaticalInflectionBinderService();
}
@Override
public void onStart() {
- publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mService);
+ publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mBinderService);
LocalServices.addService(GrammaticalInflectionManagerInternal.class,
new GrammaticalInflectionManagerInternalImpl());
}
- private final IBinder mService = new IGrammaticalInflectionManager.Stub() {
+ private final class GrammaticalInflectionBinderService extends
+ IGrammaticalInflectionManager.Stub {
@Override
public void setRequestedApplicationGrammaticalGender(
String appPackageName, int userId, int gender) {
GrammaticalInflectionService.this.setRequestedApplicationGrammaticalGender(
appPackageName, userId, gender);
}
+
+ @Override
+ public void setSystemWideGrammaticalGender(int userId, int grammaticalGender) {
+ checkCallerIsSystem();
+ checkSystemTermsOfAddressIsEnabled();
+ GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
+ userId);
+ }
+
+ @Override
+ public int getSystemGrammaticalGender(int userId) {
+ checkSystemTermsOfAddressIsEnabled();
+ return GrammaticalInflectionService.this.getSystemGrammaticalGender(userId);
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new GrammaticalInflectionShellCommand(mBinderService))
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
};
private final class GrammaticalInflectionManagerInternalImpl
@@ -94,12 +148,6 @@ public class GrammaticalInflectionService extends SystemService {
public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
mBackupHelper.stageAndApplyRestoredPayload(payload, userId);
}
-
- private void checkCallerIsSystem() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Caller is not system.");
- }
- }
}
protected int getApplicationGrammaticalGender(String appPackageName, int userId) {
@@ -137,4 +185,105 @@ public class GrammaticalInflectionService extends SystemService {
updater.setGrammaticalGender(gender).commit();
}
+
+ protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
+ if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+ grammaticalGender)) {
+ throw new IllegalArgumentException("Unknown grammatical gender");
+ }
+
+ synchronized (mLock) {
+ final File file = getGrammaticalGenderFile(userId);
+ final AtomicFile atomicFile = new AtomicFile(file);
+ FileOutputStream stream = null;
+ try {
+ stream = atomicFile.startWrite();
+ stream.write(toXmlByteArray(grammaticalGender, stream));
+ atomicFile.finishWrite(stream);
+ mGrammaticalGenderCache.put(userId, grammaticalGender);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write file " + atomicFile, e);
+ if (stream != null) {
+ atomicFile.failWrite(stream);
+ }
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ // TODO(b/298591009): Add a new AppOp value for the apps that want to access the grammatical
+ // gender.
+ public int getSystemGrammaticalGender(int userId) {
+ synchronized (mLock) {
+ final File file = getGrammaticalGenderFile(userId);
+ if (!file.exists()) {
+ Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
+ try {
+ InputStream in = new FileInputStream(file);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+ }
+ }
+ return mGrammaticalGenderCache.get(userId);
+ }
+ }
+
+ private File getGrammaticalGenderFile(int userId) {
+ final File dir = new File(Environment.getDataSystemCeDirectory(userId),
+ TAG_GRAMMATICAL_INFLECTION);
+ return new File(dir, USER_SETTINGS_FILE_NAME);
+ }
+
+ private byte[] toXmlByteArray(int grammaticalGender, FileOutputStream fileStream) {
+
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ TypedXmlSerializer out = Xml.resolveSerializer(fileStream);
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(null, TAG_GRAMMATICAL_INFLECTION);
+ out.attributeInt(null, ATTR_NAME, grammaticalGender);
+ out.endTag(null, TAG_GRAMMATICAL_INFLECTION);
+ out.endDocument();
+
+ return outputStream.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private int getGrammaticalGenderFromXml(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (TAG_GRAMMATICAL_INFLECTION.equals(tagName)) {
+ return parser.getAttributeInt(null, ATTR_NAME);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ private void checkCallerIsSystem() {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID) {
+ throw new SecurityException("Caller is not system and shell.");
+ }
+ }
+
+ private void checkSystemTermsOfAddressIsEnabled() {
+ if (!systemTermsOfAddressEnabled()) {
+ throw new RuntimeException("The flag must be enabled to allow calling the API.");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java
new file mode 100644
index 000000000000..d22372860ead
--- /dev/null
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.grammaticalinflection;
+
+import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
+
+import android.app.ActivityManager;
+import android.app.GrammaticalInflectionManager;
+import android.app.IGrammaticalInflectionManager;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell commands for {@link GrammaticalInflectionService}
+ */
+class GrammaticalInflectionShellCommand extends ShellCommand {
+
+ private static final SparseArray<String> GRAMMATICAL_GENDER_MAP = new SparseArray<>();
+ static {
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
+ "Not specified (0)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_NEUTRAL, "Neuter (1)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_FEMININE, "Feminine (2)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_MASCULINE, "Masculine (3)");
+ }
+
+ private final IGrammaticalInflectionManager mBinderService;
+
+ GrammaticalInflectionShellCommand(IGrammaticalInflectionManager grammaticalInflectionManager) {
+ mBinderService = grammaticalInflectionManager;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-system-grammatical-gender":
+ return runSetSystemWideGrammaticalGender();
+ case "get-system-grammatical-gender":
+ return runGetSystemGrammaticalGender();
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Grammatical inflection manager (grammatical_inflection) shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(
+ " set-system-grammatical-gender [--user <USER_ID>] [--grammaticalGender "
+ + "<GRAMMATICAL_GENDER>]");
+ pw.println(" Set the system grammatical gender for system.");
+ pw.println(" --user <USER_ID>: apply for the given user, "
+ + "the current user is used when unspecified.");
+ pw.println(
+ " --grammaticalGender <GRAMMATICAL_GENDER>: The terms of address the user "
+ + "preferred in system, not specified (0) is used when unspecified.");
+ pw.println(
+ " eg. 0 = not_specified, 1 = neuter, 2 = feminine, 3 = masculine"
+ + ".");
+ pw.println(
+ " get-system-grammatical-gender [--user <USER_ID>]");
+ pw.println(" Get the system grammatical gender for system.");
+ pw.println(" --user <USER_ID>: apply for the given user, "
+ + "the current user is used when unspecified.");
+ }
+
+ private int runSetSystemWideGrammaticalGender() {
+ int userId = ActivityManager.getCurrentUser();
+ int grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ switch (option) {
+ case "--user": {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ case "-g":
+ case "--grammaticalGender": {
+ grammaticalGender = parseGrammaticalGender();
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ }
+ } while (true);
+
+ try {
+ mBinderService.setSystemWideGrammaticalGender(userId, grammaticalGender);
+ } catch (RemoteException e) {
+ getOutPrintWriter().println("Remote Exception: " + e);
+ }
+ return 0;
+ }
+
+ private int runGetSystemGrammaticalGender() {
+ int userId = ActivityManager.getCurrentUser();
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ switch (option) {
+ case "--user": {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ }
+ } while (true);
+
+ try {
+ int grammaticalGender = mBinderService.getSystemGrammaticalGender(userId);
+ getOutPrintWriter().println(GRAMMATICAL_GENDER_MAP.get(grammaticalGender));
+ } catch (RemoteException e) {
+ getOutPrintWriter().println("Remote Exception: " + e);
+ }
+ return 0;
+ }
+
+ private int parseGrammaticalGender() {
+ String arg = getNextArg();
+ if (arg == null) {
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ } else {
+ int grammaticalGender = Integer.parseInt(arg);
+ if (GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+ grammaticalGender)) {
+ return grammaticalGender;
+ } else {
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/grammaticalinflection/OWNERS b/services/core/java/com/android/server/grammaticalinflection/OWNERS
index 5f16ba9123b7..41d079ed9e75 100644
--- a/services/core/java/com/android/server/grammaticalinflection/OWNERS
+++ b/services/core/java/com/android/server/grammaticalinflection/OWNERS
@@ -2,3 +2,4 @@
allenwtsu@google.com
goldmanj@google.com
calvinpan@google.com
+zoeychen@google.com
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c3abfc16ca7d..f168f435b5d7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -349,17 +349,17 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void onUserStarting(@NonNull TargetUser user) {
- mLockSettingsService.onStartUser(user.getUserIdentifier());
+ mLockSettingsService.onUserStarting(user.getUserIdentifier());
}
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
- mLockSettingsService.onUnlockUser(user.getUserIdentifier());
+ mLockSettingsService.onUserUnlocking(user.getUserIdentifier());
}
@Override
public void onUserStopped(@NonNull TargetUser user) {
- mLockSettingsService.onCleanupUser(user.getUserIdentifier());
+ mLockSettingsService.onUserStopped(user.getUserIdentifier());
}
}
@@ -784,7 +784,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@VisibleForTesting
- void onCleanupUser(int userId) {
+ void onUserStopped(int userId) {
hideEncryptionNotification(new UserHandle(userId));
// User is stopped with its CE key evicted. Restore strong auth requirement to the default
// flags after boot since stopping and restarting a user later is equivalent to rebooting
@@ -796,7 +796,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private void onStartUser(final int userId) {
+ private void onUserStarting(final int userId) {
maybeShowEncryptionNotificationForUser(userId, "user started");
}
@@ -832,7 +832,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private void onUnlockUser(final int userId) {
+ private void onUserUnlocking(final int userId) {
// Perform tasks which require locks in LSS on a handler, as we are callbacks from
// ActivityManager.unlockUser()
mHandler.post(new Runnable() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 4892c22449cb..83a3125884ac 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,6 +22,7 @@ import static android.content.Intent.ACTION_SCREEN_ON;
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static android.media.MediaRouter2Utils.getOriginalId;
import static android.media.MediaRouter2Utils.getProviderId;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.media.MediaFeatureFlagManager.FEATURE_SCANNING_MINIMUM_PACKAGE_IMPORTANCE;
@@ -487,12 +488,13 @@ class MediaRouter2ServiceImpl {
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
+ final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- registerManagerLocked(manager, callerUid, callerPid, callerPackageName, userId);
+ registerManagerLocked(
+ manager, callerUid, callerPid, callerPackageName, callerUserId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1156,8 +1158,12 @@ class MediaRouter2ServiceImpl {
}
@GuardedBy("mLock")
- private void registerManagerLocked(@NonNull IMediaRouter2Manager manager,
- int callerUid, int callerPid, @NonNull String callerPackageName, int userId) {
+ private void registerManagerLocked(
+ @NonNull IMediaRouter2Manager manager,
+ int callerUid,
+ int callerPid,
+ @NonNull String callerPackageName,
+ int callerUserId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1167,14 +1173,17 @@ class MediaRouter2ServiceImpl {
return;
}
- Slog.i(TAG, TextUtils.formatSimple(
- "registerManager | callerUid: %d, callerPid: %d, package: %s, user: %d",
- callerUid, callerPid, callerPackageName, userId));
+ Slog.i(
+ TAG,
+ TextUtils.formatSimple(
+ "registerManager | callerUid: %d, callerPid: %d, callerPackage: %s,"
+ + " callerUserId: %d",
+ callerUid, callerPid, callerPackageName, callerUserId));
mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid,
"Must hold MEDIA_CONTENT_CONTROL permission.");
- UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+ UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId);
managerRecord = new ManagerRecord(
userRecord, manager, callerUid, callerPid, callerPackageName);
try {
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 9c96d46f15b8..d0e95dd55b6c 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,5 +1,5 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
jsharkey@android.com
sudheersai@google.com
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c6f6fe2c01f3..fe91050917f5 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1021,7 +1021,7 @@ abstract public class ManagedServices {
synchronized (mSnoozing) {
mSnoozing.remove(user);
}
- rebindServices(true, user);
+ unbindUserServices(user);
}
public void onUserSwitched(int user) {
@@ -1408,12 +1408,24 @@ abstract public class ManagedServices {
void unbindOtherUserServices(int currentUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
- final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+ unbindServicesImpl(currentUser, true /* allExceptUser */);
+ t.traceEnd();
+ }
+
+ void unbindUserServices(int user) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("ManagedServices.unbindUserServices" + user);
+ unbindServicesImpl(user, false /* allExceptUser */);
+ t.traceEnd();
+ }
+ void unbindServicesImpl(int user, boolean allExceptUser) {
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
synchronized (mMutex) {
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
- if (info.userid != currentUser) {
+ if ((allExceptUser && (info.userid != user))
+ || (!allExceptUser && (info.userid == user))) {
Set<ComponentName> toUnbind =
componentsToUnbind.get(info.userid, new ArraySet<>());
toUnbind.add(info.component);
@@ -1422,7 +1434,6 @@ abstract public class ManagedServices {
}
}
unbindFromServices(componentsToUnbind);
- t.traceEnd();
}
protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 802dfb182297..a3c71c2e0218 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -177,6 +177,7 @@ import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
@@ -263,6 +264,7 @@ import android.telecom.TelecomManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -4159,7 +4161,7 @@ public class NotificationManagerService extends SystemService {
String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, Binder.getCallingUid(), false, false, true);
+ pkg, Binder.getCallingUid(), false, false, true, true, null);
}
@Override
@@ -4280,7 +4282,36 @@ public class NotificationManagerService extends SystemService {
String pkg, int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, uid, includeDeleted, true, false);
+ pkg, uid, includeDeleted, true, false, true, null);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannelGroup>
+ getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid) {
+ enforceSystemOrSystemUI("getRecentBlockedNotificationChannelGroupsForPackage");
+ Set<String> recentlySentChannels = new HashSet<>();
+ long now = System.currentTimeMillis();
+ long startTime = now - (DateUtils.DAY_IN_MILLIS * 14);
+ UsageEvents events = mUsageStatsManagerInternal.queryEventsForUser(
+ UserHandle.getUserId(uid), startTime, now, UsageEvents.SHOW_ALL_EVENT_DATA);
+ // get all channelids that sent notifs in the past 2 weeks
+ if (events != null) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (events.hasNextEvent()) {
+ events.getNextEvent(event);
+ if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (pkg.equals(event.mPackage)) {
+ String channelId = event.mNotificationChannelId;
+ if (channelId != null) {
+ recentlySentChannels.add(channelId);
+ }
+ }
+ }
+ }
+ }
+
+ return mPreferencesHelper.getNotificationChannelGroups(
+ pkg, uid, false, true, false, true, recentlySentChannels);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b132a23b575b..de698d916cce 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1459,9 +1459,9 @@ public class PreferencesHelper implements RankingConfig {
}
}
- @Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
+ int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty,
+ boolean includeBlocked, Set<String> activeChannelFilter) {
Objects.requireNonNull(pkg);
Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
synchronized (mPackagePreferences) {
@@ -1473,7 +1473,11 @@ public class PreferencesHelper implements RankingConfig {
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
+ boolean includeChannel = (includeDeleted || !nc.isDeleted())
+ && (activeChannelFilter == null
+ || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
+ || activeChannelFilter.contains(nc.getId()));
+ if (includeChannel) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
NotificationChannelGroup ncg = groups.get(nc.getGroup());
@@ -1481,7 +1485,6 @@ public class PreferencesHelper implements RankingConfig {
ncg = r.groups.get(nc.getGroup()).clone();
ncg.setChannels(new ArrayList<>());
groups.put(nc.getGroup(), ncg);
-
}
ncg.addChannel(nc);
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index fec359198e88..8df24c9911a6 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -40,8 +40,6 @@ public interface RankingConfig {
int uid);
void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp, int callingUid, boolean isSystemOrSystemUi);
- ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess, int callingUid,
boolean isSystemOrSystemUi);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 52eef47b3759..b9464d96a019 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -18,9 +18,6 @@ package com.android.server.om;
import static android.app.AppGlobals.getPackageManager;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -31,10 +28,10 @@ import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISA
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
+import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
import static android.os.Trace.traceEnd;
-
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
import android.annotation.NonNull;
@@ -82,6 +79,7 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -261,6 +259,8 @@ public final class OverlayManagerService extends SystemService {
private final OverlayActorEnforcer mActorEnforcer;
+ private final PackageMonitor mPackageMonitor = new OverlayManagerPackageMonitor();
+
private int mPrevStartedUserId = -1;
public OverlayManagerService(@NonNull final Context context) {
@@ -277,16 +277,9 @@ public final class OverlayManagerService extends SystemService {
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
- HandlerThread packageReceiverThread = new HandlerThread(TAG);
- packageReceiverThread.start();
-
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, packageReceiverThread.getThreadHandler());
+ HandlerThread packageMonitorThread = new HandlerThread(TAG);
+ packageMonitorThread.start();
+ mPackageMonitor.register(context, packageMonitorThread.getLooper(), true);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(ACTION_USER_ADDED);
@@ -372,166 +365,171 @@ public final class OverlayManagerService extends SystemService {
return defaultPackages.toArray(new String[defaultPackages.size()]);
}
- private final class PackageReceiver extends BroadcastReceiver {
+ private final class OverlayManagerPackageMonitor extends PackageMonitor {
+
@Override
- public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
- final String action = intent.getAction();
- if (action == null) {
- Slog.e(TAG, "Cannot handle package broadcast with null action");
- return;
- }
- final Uri data = intent.getData();
- if (data == null) {
- Slog.e(TAG, "Cannot handle package broadcast with null data");
- return;
- }
- final String packageName = data.getSchemeSpecificPart();
+ public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+ handlePackageAdd(packageName, extras);
+ }
- final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- final boolean systemUpdateUninstall =
- intent.getBooleanExtra(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
+ @Override
+ public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+ handlePackageChange(packageName, extras);
+ }
- final int[] userIds;
- final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
- if (extraUid == UserHandle.USER_NULL) {
- userIds = mUserManager.getUserIds();
- } else {
- userIds = new int[] { UserHandle.getUserId(extraUid) };
- }
+ @Override
+ public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+ handlePackageRemove(packageName, extras);
+ }
+ }
- switch (action) {
- case ACTION_PACKAGE_ADDED:
- if (replacing) {
- onPackageReplaced(packageName, userIds);
- } else {
- onPackageAdded(packageName, userIds);
- }
- break;
- case ACTION_PACKAGE_CHANGED:
- // ignore the intent if it was sent by the package manager as a result of the
- // overlay manager having sent ACTION_OVERLAY_CHANGED
- if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
- onPackageChanged(packageName, userIds);
- }
- break;
- case ACTION_PACKAGE_REMOVED:
- if (replacing) {
- onPackageReplacing(packageName, systemUpdateUninstall, userIds);
- } else {
- onPackageRemoved(packageName, userIds);
- }
- break;
- default:
- // do nothing
- break;
- }
+ private int[] getUserIds(int uid) {
+ final int[] userIds;
+ if (uid == INVALID_UID) {
+ userIds = mUserManager.getUserIds();
+ } else {
+ userIds = new int[] { UserHandle.getUserId(uid) };
}
+ return userIds;
+ }
- private void onPackageAdded(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
- for (final int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageAdded(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageAdded(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageAdded internal error", e);
- }
+ private void handlePackageAdd(String packageName, Bundle extras) {
+ final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+ if (replacing) {
+ onPackageReplaced(packageName, userIds);
+ } else {
+ onPackageAdded(packageName, userIds);
+ }
+ }
+
+ private void handlePackageChange(String packageName, Bundle extras) {
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+ if (!ACTION_OVERLAY_CHANGED.equals(extras.getString(EXTRA_REASON))) {
+ onPackageChanged(packageName, userIds);
+ }
+ }
+
+ private void handlePackageRemove(String packageName, Bundle extras) {
+ final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ final boolean systemUpdateUninstall =
+ extras.getBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+
+ if (replacing) {
+ onPackageReplacing(packageName, systemUpdateUninstall, userIds);
+ } else {
+ onPackageRemoved(packageName, userIds);
+ }
+ }
+
+ private void onPackageAdded(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
+ for (final int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageAdded(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageAdded(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageAdded internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageChanged(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageChanged(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageChanged internal error", e);
- }
+ private void onPackageChanged(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageChanged(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageChanged internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageReplacing(@NonNull final String packageName,
- boolean systemUpdateUninstall, @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
- systemUpdateUninstall, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplacing internal error", e);
- }
+ private void onPackageReplacing(@NonNull final String packageName,
+ boolean systemUpdateUninstall, @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
+ systemUpdateUninstall, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplacing internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageReplaced(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageReplaced(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplaced internal error", e);
- }
+ private void onPackageReplaced(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageReplaced(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplaced internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageRemoved(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- mPackageManager.onPackageRemoved(packageName, userId);
- updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
- }
+ private void onPackageRemoved(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ mPackageManager.onPackageRemoved(packageName, userId);
+ updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
}
@@ -684,7 +682,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
try {
mImpl.setEnabledExclusive(
- overlay, false /* withinCategory */, realUserId)
+ overlay, false /* withinCategory */, realUserId)
.ifPresent(
OverlayManagerService.this::updateTargetPackagesLocked);
return true;
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 9a69d77c61f0..e367609e89b6 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -17,9 +17,14 @@
package com.android.server.pm;
import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Process.SYSTEM_UID;
import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -28,6 +33,7 @@ import android.Manifest;
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.BroadcastOptions;
@@ -38,12 +44,18 @@ import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.PowerExemptionManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.provider.DeviceConfig;
+import android.stats.storage.StorageEnums;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -51,10 +63,15 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.function.BiFunction;
-import java.util.function.Supplier;
/**
* Helper class to send broadcasts for various situations.
@@ -70,14 +87,20 @@ public final class BroadcastHelper {
private final UserManagerInternal mUmInternal;
private final ActivityManagerInternal mAmInternal;
private final Context mContext;
+ private final Handler mHandler;
+ private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
+ private final AppsFilterSnapshot mAppsFilter;
BroadcastHelper(PackageManagerServiceInjector injector) {
mUmInternal = injector.getUserManagerInternal();
mAmInternal = injector.getActivityManagerInternal();
mContext = injector.getContext();
+ mHandler = injector.getHandler();
+ mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
+ mAppsFilter = injector.getAppsFilter();
}
- public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+ void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds, int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
@@ -114,9 +137,16 @@ public final class BroadcastHelper {
* the system and applications allowed to see instant applications to receive package
* lifecycle events for instant applications.
*/
- public void doSendBroadcast(String action, String pkg, Bundle extras,
- int flags, String targetPkg, IIntentReceiver finishedReceiver,
- int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
+ private void doSendBroadcast(
+ @NonNull String action,
+ @Nullable String pkg,
+ @Nullable Bundle extras,
+ int flags,
+ @Nullable String targetPkg,
+ @Nullable IIntentReceiver finishedReceiver,
+ @NonNull int[] userIds,
+ boolean isInstantApp,
+ @Nullable SparseArray<int[]> broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
@Nullable Bundle bOptions) {
for (int userId : userIds) {
@@ -166,9 +196,11 @@ public final class BroadcastHelper {
}
}
- public void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotComputer,
- boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames,
- @NonNull int[] uids) {
+ void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
+ boolean mediaStatus,
+ boolean replacing,
+ @NonNull String[] pkgNames,
+ @NonNull int[] uids) {
if (ArrayUtils.isEmpty(pkgNames) || ArrayUtils.isEmpty(uids)) {
return;
}
@@ -184,7 +216,7 @@ public final class BroadcastHelper {
null /* targetPkg */, null /* finishedReceiver */, null /* userIds */,
null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
- snapshotComputer.get(), callingUid, intentExtras),
+ snapshot, callingUid, intentExtras),
null /* bOptions */);
}
@@ -193,8 +225,9 @@ public final class BroadcastHelper {
* automatically without needing an explicit launch.
* Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
*/
- public void sendBootCompletedBroadcastToSystemApp(
- String packageName, boolean includeStopped, int userId) {
+ private void sendBootCompletedBroadcastToSystemApp(@NonNull String packageName,
+ boolean includeStopped,
+ int userId) {
// If user is not running, the app didn't miss any broadcast
if (!mUmInternal.isUserRunning(userId)) {
return;
@@ -229,7 +262,7 @@ public final class BroadcastHelper {
}
}
- public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
+ private @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
@PowerExemptionManager.ReasonCode int reasonCode) {
long duration = 10_000;
if (mAmInternal != null) {
@@ -242,9 +275,14 @@ public final class BroadcastHelper {
return bOptions;
}
- public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp,
- ArrayList<String> componentNames, int packageUid, String reason,
- int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ private void sendPackageChangedBroadcast(@NonNull String packageName,
+ boolean dontKillApp,
+ @NonNull ArrayList<String> componentNames,
+ int packageUid,
+ @Nullable String reason,
+ @Nullable int[] userIds,
+ @Nullable int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList) {
if (DEBUG_INSTALL) {
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ componentNames);
@@ -269,7 +307,7 @@ public final class BroadcastHelper {
null /* bOptions */);
}
- public static void sendDeviceCustomizationReadyBroadcast() {
+ static void sendDeviceCustomizationReadyBroadcast() {
final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
final IActivityManager am = ActivityManager.getService();
@@ -285,15 +323,23 @@ public final class BroadcastHelper {
}
}
- public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId,
- int launcherUid, @Nullable ComponentName launcherComponent,
- @Nullable String appPredictionServicePackage) {
+ void sendSessionCommitBroadcast(@NonNull Computer snapshot,
+ @NonNull PackageInstaller.SessionInfo sessionInfo,
+ int userId,
+ @Nullable String appPredictionServicePackage) {
+ UserManagerService ums = UserManagerService.getInstance();
+ if (ums == null || sessionInfo.isStaged()) {
+ return;
+ }
+ final UserInfo parent = ums.getProfileParent(userId);
+ final int launcherUserId = (parent != null) ? parent.id : userId;
+ final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId);
if (launcherComponent != null) {
Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(launcherComponent.getPackageName());
- mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
+ mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId));
}
// TODO(b/122900055) Change/Remove this and replace with new permission role.
if (appPredictionServicePackage != null) {
@@ -301,30 +347,278 @@ public final class BroadcastHelper {
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(appPredictionServicePackage);
- mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
+ mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUserId));
}
}
- public void sendPreferredActivityChangedBroadcast(int userId) {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- return;
+ void sendPreferredActivityChangedBroadcast(int userId) {
+ mHandler.post(() -> {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ return;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ try {
+ am.broadcastIntentWithFeature(null, null, intent, null, null,
+ 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
+ null, false, false, userId);
+ } catch (RemoteException e) {
+ }
+ });
+ }
+
+ void sendPostInstallBroadcasts(@NonNull Computer snapshot,
+ @NonNull InstallRequest request,
+ @NonNull String packageName,
+ @NonNull String requiredPermissionControllerPackage,
+ @NonNull String[] requiredVerifierPackages,
+ @NonNull String requiredInstallerPackage,
+ @NonNull PackageSender packageSender,
+ boolean isLaunchedForRestore,
+ boolean isKillApp,
+ boolean isUpdate,
+ boolean isArchived) {
+ // Send the removed broadcasts
+ if (request.getRemovedInfo() != null) {
+ if (request.getRemovedInfo().mIsExternal) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final String[] pkgNames = new String[]{
+ request.getRemovedInfo().mRemovedPackage};
+ final int[] uids = new int[]{request.getRemovedInfo().mUid};
+ notifyResourcesChanged(
+ false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ sendResourcesChangedBroadcast(
+ snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ }
+ sendPackageRemovedBroadcasts(
+ request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/,
+ false /*isArchived*/);
}
- final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- try {
- am.broadcastIntentWithFeature(null, null, intent, null, null,
- 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
- null, false, false, userId);
- } catch (RemoteException e) {
+ final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
+ final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
+ final int[] updateUserIds = request.getUpdateBroadcastUserIds();
+ final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
+
+ final String installerPackageName =
+ request.getInstallerPackageName() != null
+ ? request.getInstallerPackageName()
+ : request.getRemovedInfo() != null
+ ? request.getRemovedInfo().mInstallerPackageName
+ : null;
+
+ Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_UID, request.getAppId());
+ if (isUpdate) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ if (isArchived) {
+ extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+ }
+ extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, request.getDataLoaderType());
+
+ final String staticSharedLibraryName = request.getPkg().getStaticSharedLibraryName();
+ // If a package is a static shared library, then only the installer of the package
+ // should get the broadcast.
+ if (installerPackageName != null && staticSharedLibraryName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ request.getNewUsers(), null /* instantUserIds*/,
+ null /* broadcastAllowList */, null);
+ }
+
+ // Send installed broadcasts if the package is not a static shared lib.
+ if (staticSharedLibraryName == null) {
+ // Send PACKAGE_ADDED broadcast for users that see the package for the first time
+ // sendPackageAddedForNewUsers also deals with system apps
+ final int appId = UserHandle.getAppId(request.getAppId());
+ final boolean isSystem = request.isInstallSystem();
+ final boolean isVirtualPreload =
+ ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ sendPackageAddedForNewUsers(snapshot, packageName,
+ isSystem || isVirtualPreload,
+ isVirtualPreload /*startReceiver*/, appId,
+ firstUserIds, firstInstantUserIds, isArchived, request.getDataLoaderType());
+
+ // Send PACKAGE_ADDED broadcast for users that don't see
+ // the package for the first time
+
+ // Send to all running apps.
+ final SparseArray<int[]> newBroadcastAllowList =
+ mAppsFilter.getVisibilityAllowList(snapshot,
+ snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+ updateUserIds, snapshot.getPackageStates());
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, newBroadcastAllowList, null);
+ // Send to the installer, even if it's not running.
+ if (installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // Send to PermissionController for all update users, even if it may not be running
+ // for some users
+ if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ requiredPermissionControllerPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // Notify required verifier(s) that are not the installer of record for the package.
+ for (String verifierPackageName : requiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED,
+ packageName,
+ extras, 0 /*flags*/,
+ verifierPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */,
+ null);
+ }
+ }
+ // If package installer is defined, notify package installer about new
+ // app installed
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+ requiredInstallerPackage, null /*finishedReceiver*/,
+ firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
+
+ // Send replaced for users that don't see the package for the first time
+ if (isUpdate) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds,
+ request.getRemovedInfo().mBroadcastAllowList, null);
+ if (installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
+ for (String verifierPackageName : requiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/, verifierPackageName,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/, null);
+ }
+ }
+ sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null /*package*/, null /*extras*/, 0 /*flags*/,
+ packageName /*targetPackage*/,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/,
+ getTemporaryAppAllowlistBroadcastOptions(
+ REASON_PACKAGE_REPLACED).toBundle());
+ } else if (isLaunchedForRestore && !request.isInstallSystem()) {
+ // First-install and we did a restore, so we're responsible for the
+ // first-launch broadcast.
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Post-restore of " + packageName
+ + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
+ }
+ sendFirstLaunchBroadcast(packageName, installerPackageName,
+ firstUserIds, firstInstantUserIds);
+ }
+
+ // Send broadcast package appeared if external for all users
+ if (request.getPkg().isExternalStorage()) {
+ if (!isUpdate) {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ VolumeInfo volume =
+ storage.findVolumeByUuid(
+ StorageManager.convert(
+ request.getPkg().getVolumeUuid()).toString());
+ int packageExternalStorageType =
+ PackageManagerServiceUtils.getPackageExternalStorageType(volume,
+ /* isExternalStorage */ true);
+ // If the package was installed externally, log it.
+ if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+ packageExternalStorageType, packageName);
+ }
+ }
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + packageName + " is external");
+ }
+ if (!isArchived) {
+ final String[] pkgNames = new String[]{packageName};
+ final int[] uids = new int[]{request.getPkg().getUid()};
+ sendResourcesChangedBroadcast(snapshot,
+ true /* mediaStatus */, true /* replacing */, pkgNames, uids);
+ notifyResourcesChanged(true /* mediaStatus */,
+ true /* replacing */, pkgNames, uids);
+ }
+ }
+ } else { // if static shared lib
+ final ArrayList<AndroidPackage> libraryConsumers = request.getLibraryConsumers();
+ if (!ArrayUtils.isEmpty(libraryConsumers)) {
+ // No need to kill consumers if it's installation of new version static shared lib.
+ final boolean dontKillApp = !isUpdate;
+ for (int i = 0; i < libraryConsumers.size(); i++) {
+ AndroidPackage pkg = libraryConsumers.get(i);
+ // send broadcast that all consumers of the static shared library have changed
+ sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
+ dontKillApp,
+ new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+ pkg.getUid(), null);
+ }
+ }
}
}
- public void sendPackageAddedForNewUsers(String packageName, @AppIdInt int appId, int[] userIds,
- int[] instantUserIds, boolean isArchived, int dataLoaderType,
- SparseArray<int[]> broadcastAllowlist) {
+ private void sendPackageAddedForNewUsers(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ boolean sendBootCompleted,
+ boolean includeStopped,
+ @AppIdInt int appId,
+ int[] userIds,
+ int[] instantUserIds,
+ boolean isArchived,
+ int dataLoaderType) {
+ if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
+ return;
+ }
+ SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot,
+ snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+ userIds, snapshot.getPackageStates());
+ mHandler.post(
+ () -> sendPackageAddedForNewUsers(packageName, appId, userIds,
+ instantUserIds, isArchived, dataLoaderType, broadcastAllowList));
+ mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
+ instantUserIds, isArchived, dataLoaderType, broadcastAllowList, mHandler);
+ if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
+ mHandler.post(() -> {
+ for (int userId : userIds) {
+ sendBootCompletedBroadcastToSystemApp(
+ packageName, includeStopped, userId);
+ }
+ }
+ );
+ }
+ }
+
+ private void sendPackageAddedForNewUsers(@NonNull String packageName,
+ @AppIdInt int appId,
+ int[] userIds,
+ int[] instantUserIds,
+ boolean isArchived,
+ int dataLoaderType,
+ @NonNull SparseArray<int[]> broadcastAllowlist) {
Bundle extras = new Bundle(1);
// Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
final int uid = UserHandle.getUid(
@@ -349,7 +643,30 @@ public final class BroadcastHelper {
}
}
- public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+ void sendPackageAddedForUser(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ @NonNull PackageStateInternal packageState,
+ int userId,
+ boolean isArchived,
+ int dataLoaderType,
+ @Nullable String appPredictionServicePackage) {
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ final boolean isSystem = packageState.isSystem();
+ final boolean isInstantApp = userState.isInstantApp();
+ final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+ final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/,
+ false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
+ isArchived, dataLoaderType);
+
+ // Send a session commit broadcast
+ final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
+ info.installReason = userState.getInstallReason();
+ info.appPackageName = packageName;
+ sendSessionCommitBroadcast(snapshot, info, userId, appPredictionServicePackage);
+ }
+
+ void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
int[] userIds, int[] instantUserIds) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */,
@@ -366,7 +683,7 @@ public final class BroadcastHelper {
* access all the packages in the extras.
*/
@Nullable
- public static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
+ private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
@NonNull Bundle extras) {
if (UserHandle.isCore(callingUid)) {
// see all
@@ -392,7 +709,7 @@ public final class BroadcastHelper {
}
/** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
- public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
+ private static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
PackageManager packageManager = context.getPackageManager();
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true)
@@ -424,4 +741,323 @@ public final class BroadcastHelper {
pkgList.size() > 0 ? pkgList.toArray(new String[pkgList.size()]) : null,
uidList != null && uidList.size() > 0 ? uidList.toArray() : null);
}
+
+ void sendApplicationHiddenForUser(@NonNull String packageName,
+ @NonNull PackageStateInternal packageState,
+ int userId,
+ @NonNull PackageSender packageSender) {
+ final PackageRemovedInfo info = new PackageRemovedInfo();
+ info.mRemovedPackage = packageName;
+ info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName;
+ info.mRemovedUsers = new int[] {userId};
+ info.mBroadcastUsers = new int[] {userId};
+ info.mUid = UserHandle.getUid(userId, packageState.getAppId());
+ info.mRemovedPackageVersionCode = packageState.getVersionCode();
+ sendPackageRemovedBroadcasts(info, packageSender, true /*killApp*/,
+ false /*removedBySystem*/, false /*isArchived*/);
+ }
+
+ void sendPackageChangedBroadcast(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ boolean dontKillApp,
+ @NonNull ArrayList<String> componentNames,
+ int packageUid,
+ @NonNull String reason) {
+ PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
+ Process.SYSTEM_UID);
+ if (setting == null) {
+ return;
+ }
+ final int userId = UserHandle.getUserId(packageUid);
+ final boolean isInstantApp =
+ snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
+ final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+ final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ final SparseArray<int[]> broadcastAllowList =
+ isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
+ mHandler.post(() -> sendPackageChangedBroadcast(
+ packageName, dontKillApp, componentNames, packageUid, reason, userIds,
+ instantUserIds, broadcastAllowList));
+ mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
+ packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
+ }
+
+ private void sendPackageBroadcastAndNotify(@NonNull String action,
+ @NonNull String pkg,
+ @NonNull Bundle extras,
+ int flags,
+ @Nullable String targetPkg,
+ @Nullable IIntentReceiver finishedReceiver,
+ @NonNull int[] userIds,
+ @NonNull int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable Bundle bOptions) {
+ mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags,
+ targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
+ null /* filterExtrasForReceiver */, bOptions));
+ if (targetPkg == null) {
+ // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
+ // many times to different targets, e.g. installer app, permission controller, other
+ // registered apps. We should filter it to avoid calling back many times for the same
+ // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
+ // installer app or null for registered apps. The callback only need to send back to the
+ // registered apps so we check the null condition here.
+ notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
+ }
+ }
+
+ void sendSystemPackageUpdatedBroadcasts(@NonNull PackageRemovedInfo packageRemovedInfo) {
+ if (!packageRemovedInfo.mIsRemovedPackageSystemUpdate) {
+ return;
+ }
+
+ final String removedPackage = packageRemovedInfo.mRemovedPackage;
+ final int removedAppId = packageRemovedInfo.mRemovedAppId;
+ final int uid = packageRemovedInfo.mUid;
+ final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
+ final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
+
+ Bundle extras = new Bundle(2);
+ extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
+ 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
+
+ if (installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ }
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
+ sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+ removedPackage, null, null, null, null /* broadcastAllowList */,
+ getTemporaryBroadcastOptionsForSystemPackageUpdate(REASON_PACKAGE_REPLACED)
+ .toBundle());
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private @NonNull BroadcastOptions getTemporaryBroadcastOptionsForSystemPackageUpdate(
+ @PowerExemptionManager.ReasonCode int reasonCode) {
+ long duration = 10_000;
+ if (mAmInternal != null) {
+ duration = mAmInternal.getBootTimeTempAllowListDuration();
+ }
+ final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+ bOptions.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ reasonCode, "");
+ return bOptions;
+ }
+
+
+ void sendPackageRemovedBroadcasts(
+ @NonNull PackageRemovedInfo packageRemovedInfo,
+ @NonNull PackageSender packageSender,
+ boolean killApp,
+ boolean removedBySystem,
+ boolean isArchived) {
+ final String removedPackage = packageRemovedInfo.mRemovedPackage;
+ final int removedAppId = packageRemovedInfo.mRemovedAppId;
+ final int uid = packageRemovedInfo.mUid;
+ final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
+ final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers;
+ final int[] instantUserIds = packageRemovedInfo.mInstantUserIds;
+ final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
+ final boolean dataRemoved = packageRemovedInfo.mDataRemoved;
+ final boolean isUpdate = packageRemovedInfo.mIsUpdate;
+ final boolean isRemovedPackageSystemUpdate =
+ packageRemovedInfo.mIsRemovedPackageSystemUpdate;
+ final boolean isRemovedForAllUsers = packageRemovedInfo.mRemovedForAllUsers;
+ final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib;
+
+ Bundle extras = new Bundle();
+ final int removedUid = removedAppId >= 0 ? removedAppId : uid;
+ extras.putInt(Intent.EXTRA_UID, removedUid);
+ extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
+ extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
+ extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
+ final boolean isReplace = isUpdate || isRemovedPackageSystemUpdate;
+ if (isReplace || isArchived) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ if (isArchived) {
+ extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+ }
+ extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, isRemovedForAllUsers);
+
+ // Send PACKAGE_REMOVED broadcast to the respective installer.
+ if (removedPackage != null && installerPackageName != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0 /*flags*/,
+ installerPackageName, null, broadcastUserIds, instantUserIds, null, null);
+ }
+ if (isStaticSharedLib) {
+ // When uninstalling static shared libraries, only the package's installer needs to be
+ // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients.
+ return;
+ }
+ if (removedPackage != null) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0, null /*targetPackage*/, null,
+ broadcastUserIds, instantUserIds, broadcastAllowList, null);
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
+ removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
+ null /*finishedReceiver*/, broadcastUserIds, instantUserIds,
+ broadcastAllowList, null /*bOptions*/);
+ if (dataRemoved && !isRemovedPackageSystemUpdate) {
+ sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
+ null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
+ packageSender.notifyPackageRemoved(removedPackage, removedUid);
+ }
+ }
+ if (removedAppId >= 0) {
+ // If a system app's updates are uninstalled the UID is not actually removed. Some
+ // services need to know the package name affected.
+ if (isReplace) {
+ extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
+ }
+
+ sendPackageBroadcastAndNotify(Intent.ACTION_UID_REMOVED,
+ null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
+ }
+ }
+
+ /**
+ * Send broadcast intents for packages suspension changes.
+ *
+ * @param intent The action name of the suspension intent.
+ * @param pkgList The names of packages which have suspension changes.
+ * @param uidList The uids of packages which have suspension changes.
+ * @param userId The user where packages reside.
+ */
+ void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot,
+ @NonNull String intent,
+ @NonNull String[] pkgList,
+ @NonNull int[] uidList,
+ boolean quarantined,
+ int userId) {
+ final Bundle extras = new Bundle(3);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ if (quarantined) {
+ extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
+ }
+ final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
+ final Bundle options = new BroadcastOptions()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+ .toBundle();
+ mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
+ extras, flags, null /* targetPkg */, null /* finishedReceiver */,
+ new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
+ (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
+ snapshot, callingUid, intentExtras),
+ options));
+ notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
+ null /* instantUserIds */, null /* broadcastAllowList */);
+ }
+
+ void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
+ @NonNull String[] affectedPackages,
+ boolean suspended,
+ int userId) {
+ final String action = suspended
+ ? Intent.ACTION_MY_PACKAGE_SUSPENDED
+ : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
+ mHandler.post(() -> {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+ + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+ return;
+ }
+ final int[] targetUserIds = new int[] {userId};
+ for (String packageName : affectedPackages) {
+ final Bundle appExtras = suspended
+ ? SuspendPackageHelper.getSuspendedPackageAppExtras(
+ snapshot, packageName, userId, SYSTEM_UID)
+ : null;
+ final Bundle intentExtras;
+ if (appExtras != null) {
+ intentExtras = new Bundle(1);
+ intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+ } else {
+ intentExtras = null;
+ }
+ doSendBroadcast(action, null, intentExtras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+ targetUserIds, false, null, null, null);
+ }
+ });
+ }
+
+ /**
+ * Send broadcast intents for packages distracting changes.
+ *
+ * @param pkgList The names of packages which have suspension changes.
+ * @param uidList The uids of packages which have suspension changes.
+ * @param userId The user where packages reside.
+ */
+ void sendDistractingPackagesChanged(@NonNull Computer snapshot,
+ @NonNull String[] pkgList,
+ @NonNull int[] uidList,
+ int userId,
+ int distractionFlags) {
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+
+ mHandler.post(() -> sendPackageBroadcast(
+ Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
+ extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
+ null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
+ null /* broadcastAllowList */,
+ (callingUid, intentExtras) -> filterExtrasChangedPackageList(
+ snapshot, callingUid, intentExtras),
+ null /* bOptions */));
+ }
+
+ void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
+ boolean mediaStatus,
+ boolean replacing,
+ @NonNull ArrayList<AndroidPackage> packages) {
+ final int size = packages.size();
+ final String[] packageNames = new String[size];
+ final int[] packageUids = new int[size];
+ for (int i = 0; i < size; i++) {
+ final AndroidPackage pkg = packages.get(i);
+ packageNames[i] = pkg.getPackageName();
+ packageUids[i] = pkg.getUid();
+ }
+ sendResourcesChangedBroadcast(snapshot, mediaStatus,
+ replacing, packageNames, packageUids);
+ notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
+ }
+
+ private void notifyPackageMonitor(@NonNull String action,
+ @NonNull String pkg,
+ @Nullable Bundle extras,
+ @NonNull int[] userIds,
+ @NonNull int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList) {
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
+ instantUserIds, broadcastAllowList, mHandler);
+ }
+
+ private void notifyResourcesChanged(boolean mediaStatus,
+ boolean replacing,
+ @NonNull String[] pkgNames,
+ @NonNull int[] uids) {
+ mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
+ uids, mHandler);
+ }
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 83f90a1c1b3c..8e767e74fc9b 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -63,7 +63,6 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -87,19 +86,16 @@ final class DeletePackageHelper {
private final PackageManagerService mPm;
private final UserManagerInternal mUserManagerInternal;
- private final PermissionManagerServiceInternal mPermissionManager;
private final RemovePackageHelper mRemovePackageHelper;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
- DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper) {
+ DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
- mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mRemovePackageHelper = removePackageHelper;
- }
-
- DeletePackageHelper(PackageManagerService pm) {
- this(pm, new RemovePackageHelper(pm));
+ mBroadcastHelper = broadcastHelper;
}
/**
@@ -121,7 +117,7 @@ final class DeletePackageHelper {
*/
public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
boolean removedBySystem) {
- final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
+ final PackageRemovedInfo info = new PackageRemovedInfo();
final boolean res;
final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
@@ -251,8 +247,9 @@ final class DeletePackageHelper {
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0;
- info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived);
- info.sendSystemPackageUpdatedBroadcasts();
+ mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, killApp,
+ removedBySystem, isArchived);
+ mBroadcastHelper.sendSystemPackageUpdatedBroadcasts(info);
PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser);
}
@@ -314,7 +311,7 @@ final class DeletePackageHelper {
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- new InstallPackageHelper(mPm).enableCompressedPackage(stubPkg, stubPs);
+ mPm.enableCompressedPackage(stubPkg, stubPs);
} else if (DEBUG_COMPRESSION) {
Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+ "after removal; pkg: " + stubPkg.getPackageName());
@@ -491,8 +488,7 @@ final class DeletePackageHelper {
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
deleteInstalledSystemPackage(action, allUserHandles, writeSettings);
- new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF(
- action, allUserHandles, writeSettings);
+ mPm.restoreDisabledSystemPackageLIF(action, allUserHandles, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
if (ps.isIncremental()) {
diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
index 8ebb6eaccd1a..c5ec73b4e2b8 100644
--- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java
+++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
@@ -19,10 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import android.annotation.NonNull;
-import android.content.Intent;
import android.content.pm.PackageManager.DistractionRestriction;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IntArray;
@@ -42,17 +39,16 @@ public final class DistractingPackageHelper {
// TODO(b/198166813): remove PMS dependency
private final PackageManagerService mPm;
- private final PackageManagerServiceInjector mInjector;
private final BroadcastHelper mBroadcastHelper;
private final SuspendPackageHelper mSuspendPackageHelper;
/**
* Constructor for {@link PackageManagerService}.
*/
- DistractingPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
- BroadcastHelper broadcastHelper, SuspendPackageHelper suspendPackageHelper) {
+ DistractingPackageHelper(PackageManagerService pm,
+ BroadcastHelper broadcastHelper,
+ SuspendPackageHelper suspendPackageHelper) {
mPm = pm;
- mInjector = injector;
mBroadcastHelper = broadcastHelper;
mSuspendPackageHelper = suspendPackageHelper;
}
@@ -127,8 +123,8 @@ public final class DistractingPackageHelper {
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
- sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
- restrictionFlags);
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ changedPackages, changedUids.toArray(), userId, restrictionFlags);
mPm.scheduleWritePackageRestrictions(userId);
}
return unactionedPackages.toArray(new String[0]);
@@ -202,34 +198,9 @@ public final class DistractingPackageHelper {
if (!changedPackages.isEmpty()) {
final String[] packageArray = changedPackages.toArray(
new String[changedPackages.size()]);
- sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId,
- RESTRICTION_NONE);
+ mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+ packageArray, changedUids.toArray(), userId, RESTRICTION_NONE);
mPm.scheduleWritePackageRestrictions(userId);
}
}
-
- /**
- * Send broadcast intents for packages distracting changes.
- *
- * @param pkgList The names of packages which have suspension changes.
- * @param uidList The uids of packages which have suspension changes.
- * @param userId The user where packages reside.
- */
- void sendDistractingPackagesChanged(@NonNull String[] pkgList, int[] uidList, int userId,
- int distractionFlags) {
- final Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
-
- final Handler handler = mInjector.getHandler();
- handler.post(() -> mBroadcastHelper.sendPackageBroadcast(
- Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
- extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
- null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
- null /* broadcastAllowList */,
- (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- mPm.snapshotComputer(), callingUid, intentExtras),
- null /* bOptions */));
- }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 486b1ad3c9ec..8f71a9be4fe5 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -38,7 +38,6 @@ import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -50,7 +49,6 @@ import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
-import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
@@ -130,7 +128,6 @@ import android.content.pm.parsing.result.ParseTypeImpl;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
-import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Message;
@@ -143,9 +140,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.stats.storage.StorageEnums;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -163,7 +157,6 @@ import com.android.internal.content.F2fsUtils;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemConfig;
@@ -220,6 +213,7 @@ final class InstallPackageHelper {
private final AppDataHelper mAppDataHelper;
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
+ private final DeletePackageHelper mDeletePackageHelper;
private final IncrementalManager mIncrementalManager;
private final ApexManager mApexManager;
private final DexManager mDexManager;
@@ -233,12 +227,17 @@ final class InstallPackageHelper {
private final UpdateOwnershipHelper mUpdateOwnershipHelper;
// TODO(b/198166813): remove PMS dependency
- InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+ InstallPackageHelper(PackageManagerService pm,
+ AppDataHelper appDataHelper,
+ RemovePackageHelper removePackageHelper,
+ DeletePackageHelper deletePackageHelper,
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
- mBroadcastHelper = new BroadcastHelper(pm.mInjector);
- mRemovePackageHelper = new RemovePackageHelper(pm);
+ mBroadcastHelper = broadcastHelper;
+ mRemovePackageHelper = removePackageHelper;
+ mDeletePackageHelper = deletePackageHelper;
mIncrementalManager = pm.mInjector.getIncrementalManager();
mApexManager = pm.mInjector.getApexManager();
mDexManager = pm.mInjector.getDexManager();
@@ -251,10 +250,6 @@ final class InstallPackageHelper {
mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper();
}
- InstallPackageHelper(PackageManagerService pm) {
- this(pm, new AppDataHelper(pm));
- }
-
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an exception in the middle
@@ -263,7 +258,7 @@ final class InstallPackageHelper {
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy("mPm.mLock")
- public AndroidPackage commitReconciledScanResultLocked(
+ private AndroidPackage commitReconciledScanResultLocked(
@NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
final InstallRequest request = reconciledPkg.mInstallRequest;
// TODO(b/135203078): Move this even further away
@@ -731,8 +726,9 @@ final class InstallPackageHelper {
}
// TODO(b/278553670) Store archive state for the user.
boolean isArchived = (pkgSetting.getPkg() == null);
- mPm.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, pkgSetting, userId,
- isArchived, DataLoaderType.NONE);
+ mBroadcastHelper.sendPackageAddedForUser(mPm.snapshotComputer(), packageName,
+ pkgSetting, userId, isArchived, DataLoaderType.NONE,
+ mPm.mAppPredictionServicePackage);
synchronized (mPm.mLock) {
mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId });
}
@@ -998,8 +994,7 @@ final class InstallPackageHelper {
return;
}
final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
- final boolean isSdkLibrary = packageToScan.isSdkLibrary();
- if (!isApex && !isSdkLibrary) {
+ if (!isApex) {
createdAppId.put(packageName, optimisticallyRegisterAppId(request));
} else {
request.getScannedPackageSetting().setAppId(Process.INVALID_UID);
@@ -1746,7 +1741,7 @@ final class InstallPackageHelper {
}
// Update what is removed
- PackageRemovedInfo removedInfo = new PackageRemovedInfo(mPm);
+ PackageRemovedInfo removedInfo = new PackageRemovedInfo();
removedInfo.mUid = ps.getAppId();
removedInfo.mRemovedPackage = ps.getPackageName();
removedInfo.mInstallerPackageName =
@@ -2075,8 +2070,6 @@ final class InstallPackageHelper {
final InstallRequest installRequest = reconciledPkg.mInstallRequest;
final ParsedPackage parsedPackage = installRequest.getParsedPackage();
final String packageName = parsedPackage.getPackageName();
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
- final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
installRequest.onCommitStarted();
if (installRequest.isInstallReplace()) {
@@ -2098,7 +2091,7 @@ final class InstallPackageHelper {
allUsers, mPm.mSettings.getPackagesLocked());
if (installRequest.isInstallSystem()) {
// Remove existing system package
- removePackageHelper.removePackage(oldPackage, true);
+ mRemovePackageHelper.removePackage(oldPackage, true);
if (!disableSystemPackageLPw(oldPackage)) {
// We didn't need to disable the .apk as a current system package,
// which means we are replacing another update that is already
@@ -2114,7 +2107,7 @@ final class InstallPackageHelper {
} else {
try {
// Settings will be written during the call to updateSettingsLI().
- deletePackageHelper.executeDeletePackage(
+ mDeletePackageHelper.executeDeletePackage(
reconciledPkg.mDeletePackageAction, packageName,
true, allUsers, false);
} catch (SystemDeleteException e) {
@@ -2201,12 +2194,19 @@ final class InstallPackageHelper {
final String installerPackageName = installRequest.getInstallerPackageName();
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
+ final int userId = installRequest.getUserId();
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT
+ && !mPm.mUserManager.exists(userId)) {
+ installRequest.setError(PackageManagerException.ofInternalError(
+ "User " + userId + " doesn't exist or has been removed",
+ PackageManagerException.INTERNAL_ERROR_MISSING_USER));
+ return;
+ }
synchronized (mPm.mLock) {
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
- final int userId = installRequest.getUserId();
if (ps != null) {
if (ps.isSystem()) {
if (DEBUG_INSTALL) {
@@ -2797,24 +2797,21 @@ final class InstallPackageHelper {
final Computer snapshot = mPm.snapshotComputer();
// Send broadcasts
for (int i = 0; i < numBroadcasts; i++) {
- mPm.sendPackageChangedBroadcast(snapshot, packages[i], true /* dontKillApp */,
- components[i], uids[i], null /* reason */);
+ mBroadcastHelper.sendPackageChangedBroadcast(snapshot, packages[i],
+ true /* dontKillApp */, components[i], uids[i], null /* reason */);
}
}
void handlePackagePostInstall(InstallRequest request, boolean launchedForRestore) {
final boolean killApp =
(request.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) == 0;
- final boolean virtualPreload =
- ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- final String installerPackage = request.getInstallerPackageName();
- final int dataLoaderType = request.getDataLoaderType();
final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED;
final boolean update = request.isUpdate();
final boolean archived = request.isArchived();
final String packageName = request.getName();
+ final Computer snapshot = mPm.snapshotComputer();
final PackageStateInternal pkgSetting =
- succeeded ? mPm.snapshotComputer().getPackageStateInternal(packageName) : null;
+ succeeded ? snapshot.getPackageStateInternal(packageName) : null;
final boolean removedBeforeUpdate = (pkgSetting == null)
|| (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
request.getPkg().getPath()));
@@ -2835,207 +2832,21 @@ final class InstallPackageHelper {
// Clear the uid cache after we installed a new package.
mPm.mPerUidReadTimeoutsCache = null;
- // Send the removed broadcasts
- if (request.getRemovedInfo() != null) {
- if (request.getRemovedInfo().mIsExternal) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
- + " is ASEC-hosted -> UNAVAILABLE");
- }
- final String[] pkgNames = new String[]{
- request.getRemovedInfo().mRemovedPackage};
- final int[] uids = new int[]{request.getRemovedInfo().mUid};
- mPm.notifyResourcesChanged(false /* mediaStatus */,
- true /* replacing */, pkgNames, uids);
- mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
- false /* mediaStatus */, true /* replacing */, pkgNames, uids);
- }
- request.getRemovedInfo().sendPackageRemovedBroadcasts(
- killApp, false /*removedBySystem*/, false /*isArchived*/);
- }
-
- final String installerPackageName =
- request.getInstallerPackageName() != null
- ? request.getInstallerPackageName()
- : request.getRemovedInfo() != null
- ? request.getRemovedInfo().mInstallerPackageName
- : null;
-
mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(),
request.getNewUsers());
request.populateBroadcastUsers();
final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
- final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
- final int[] updateUserIds = request.getUpdateBroadcastUserIds();
- final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
-
- Bundle extras = new Bundle();
- extras.putInt(Intent.EXTRA_UID, request.getAppId());
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- if (archived) {
- extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
- }
- extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
-
- // If a package is a static shared library, then only the installer of the package
- // should get the broadcast.
- if (installerPackageName != null
- && request.getPkg().getStaticSharedLibraryName() != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- request.getNewUsers(), null /* instantUserIds*/,
- null /* broadcastAllowList */, null);
- }
- // Send installed broadcasts if the package is not a static shared lib.
if (request.getPkg().getStaticSharedLibraryName() == null) {
mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath());
+ }
- // Send PACKAGE_ADDED broadcast for users that see the package for the first time
- // sendPackageAddedForNewUsers also deals with system apps
- int appId = UserHandle.getAppId(request.getAppId());
- boolean isSystem = request.isInstallSystem();
- mPm.sendPackageAddedForNewUsers(mPm.snapshotComputer(), packageName,
- isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId,
- firstUserIds, firstInstantUserIds, archived, dataLoaderType);
+ mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName,
+ mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages,
+ mPm.mRequiredInstallerPackage,
+ /* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
- // Send PACKAGE_ADDED broadcast for users that don't see
- // the package for the first time
-
- // Send to all running apps.
- final SparseArray<int[]> newBroadcastAllowList;
- synchronized (mPm.mLock) {
- final Computer snapshot = mPm.snapshotComputer();
- newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(snapshot,
- snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
- updateUserIds, mPm.mSettings.getPackagesLocked());
- }
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, newBroadcastAllowList, null);
- // Send to the installer, even if it's not running.
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // Send to PermissionController for all update users, even if it may not be running
- // for some users
- if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredPermissionControllerPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // Notify required verifier(s) that are not the installer of record for the package.
- for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
- if (verifierPackageName != null && !verifierPackageName.equals(
- installerPackageName)) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- verifierPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */,
- null);
- }
- }
- // If package installer is defined, notify package installer about new
- // app installed
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
- mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
- firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
-
- // Send replaced for users that don't see the package for the first time
- if (update) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds,
- request.getRemovedInfo().mBroadcastAllowList, null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/,
- null);
- }
- for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
- if (verifierPackageName != null && !verifierPackageName.equals(
- installerPackageName)) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/, verifierPackageName,
- null /*finishedReceiver*/, updateUserIds, instantUserIds,
- null /*broadcastAllowList*/, null);
- }
- }
- mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null /*package*/, null /*extras*/, 0 /*flags*/,
- packageName /*targetPackage*/,
- null /*finishedReceiver*/, updateUserIds, instantUserIds,
- null /*broadcastAllowList*/,
- mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
- REASON_PACKAGE_REPLACED).toBundle());
- } else if (launchedForRestore && !request.isInstallSystem()) {
- // First-install and we did a restore, so we're responsible for the
- // first-launch broadcast.
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Post-restore of " + packageName
- + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
- }
- mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage,
- firstUserIds, firstInstantUserIds);
- }
-
- // Send broadcast package appeared if external for all users
- if (request.getPkg().isExternalStorage()) {
- if (!update) {
- final StorageManager storageManager =
- mInjector.getSystemService(StorageManager.class);
- VolumeInfo volume =
- storageManager.findVolumeByUuid(
- StorageManager.convert(
- request.getPkg().getVolumeUuid()).toString());
- int packageExternalStorageType =
- PackageManagerServiceUtils.getPackageExternalStorageType(volume,
- request.getPkg().isExternalStorage());
- // If the package was installed externally, log it.
- if (packageExternalStorageType != StorageEnums.UNKNOWN) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
- packageExternalStorageType, packageName);
- }
- }
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + request.getPkg() + " is external");
- }
- if (!archived) {
- final String[] pkgNames = new String[]{packageName};
- final int[] uids = new int[]{request.getPkg().getUid()};
- mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
- true /* mediaStatus */, true /* replacing */, pkgNames, uids);
- mPm.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */,
- pkgNames, uids);
- }
- }
- } else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib
- // No need to kill consumers if it's installation of new version static shared lib.
- final Computer snapshot = mPm.snapshotComputer();
- final boolean dontKillApp = !update
- && request.getPkg().getStaticSharedLibraryName() != null;
- for (int i = 0; i < request.getLibraryConsumers().size(); i++) {
- AndroidPackage pkg = request.getLibraryConsumers().get(i);
- // send broadcast that all consumers of the static shared library have changed
- mPm.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), dontKillApp,
- new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
- pkg.getUid(), null);
- }
- }
// Work that needs to happen on first install within each user
if (firstUserIds.length > 0) {
@@ -3075,7 +2886,6 @@ final class InstallPackageHelper {
}
if (!archived) {
- final Computer snapshot = mPm.snapshotComputer();
// Notify DexManager that the package was installed for new users.
// The updated users should already be indexed and the package code paths
// should not change.
@@ -3091,7 +2901,7 @@ final class InstallPackageHelper {
}
} else {
// Now send PACKAGE_REMOVED + EXTRA_REPLACING broadcast.
- final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
+ final PackageRemovedInfo info = new PackageRemovedInfo();
info.mRemovedPackage = packageName;
info.mInstallerPackageName = request.getInstallerPackageName();
info.mRemovedUsers = firstUserIds;
@@ -3100,8 +2910,8 @@ final class InstallPackageHelper {
info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode();
info.mRemovedForAllUsers = true;
- info.sendPackageRemovedBroadcasts(false /*killApp*/,
- false /*removedBySystem*/, true /*isArchived*/);
+ mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm,
+ false /*killApp*/, false /*removedBySystem*/, true /*isArchived*/);
}
}
@@ -3292,15 +3102,14 @@ final class InstallPackageHelper {
synchronized (mPm.mLock) {
mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
}
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
- removePackageHelper.removePackage(stubPkg, true /*chatty*/);
+ mRemovePackageHelper.removePackage(stubPkg, true /*chatty*/);
try {
return initPackageTracedLI(scanFile, parseFlags, scanFlags);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
// Remove the failed install
- removePackageHelper.removeCodePath(scanFile);
+ mRemovePackageHelper.removeCodePath(scanFile);
throw e;
}
}
@@ -3343,7 +3152,7 @@ final class InstallPackageHelper {
if (!dstCodePath.exists()) {
return null;
}
- new RemovePackageHelper(mPm).removeCodePath(dstCodePath);
+ mRemovePackageHelper.removeCodePath(dstCodePath);
return null;
}
@@ -4299,8 +4108,8 @@ final class InstallPackageHelper {
parsedPackage.getPackageName(), UserHandle.USER_ALL,
"scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER,
null /* request */)) {
- DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
- deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mDeletePackageHelper.deletePackageLIF(
+ parsedPackage.getPackageName(), null, true,
mPm.mUserManager.getUserIds(), 0, null, false);
}
} else if (newPkgVersionGreater || newSharedUserSetting) {
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index fe6a8a1778a2..ca8dc2973404 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -94,8 +94,6 @@ class InstallingSession {
private final UserHandle mUser;
@NonNull
final PackageManagerService mPm;
- final InstallPackageHelper mInstallPackageHelper;
- final RemovePackageHelper mRemovePackageHelper;
final boolean mIsInherit;
final int mSessionId;
final int mRequireUserAction;
@@ -108,8 +106,6 @@ class InstallingSession {
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
- mRemovePackageHelper = new RemovePackageHelper(mPm);
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mObserver = observer;
@@ -142,8 +138,6 @@ class InstallingSession {
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
- mRemovePackageHelper = new RemovePackageHelper(mPm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mMoveInfo = null;
mInstallReason = fixUpInstallReason(
@@ -242,7 +236,7 @@ class InstallingSession {
// state can change within this delay and hence we need to re-verify certain conditions.
boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
if (isStaged) {
- Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
+ Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
mRet = ret.first;
if (mRet != INSTALL_SUCCEEDED) {
@@ -540,39 +534,39 @@ class InstallingSession {
}
}
} else {
- mInstallPackageHelper.installPackagesTraced(installRequests);
+ mPm.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
doPostInstall(request);
}
}
for (InstallRequest request : installRequests) {
- mInstallPackageHelper.restoreAndPostInstall(request);
+ mPm.restoreAndPostInstall(request);
}
}
private void doPostInstall(InstallRequest request) {
if (mMoveInfo != null) {
if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
+ mPm.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
} else {
- mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mToUuid,
+ mPm.cleanUpForMoveInstall(mMoveInfo.mToUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
}
} else {
if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.removeCodePath(request.getCodeFile());
+ mPm.removeCodePath(request.getCodeFile());
}
}
}
private void cleanUpForFailedInstall(InstallRequest request) {
if (request.isInstallMove()) {
- mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
+ mPm.cleanUpForMoveInstall(request.getMoveToUuid(),
request.getMovePackageName(), request.getMoveFromCodePath());
} else {
- mRemovePackageHelper.removeCodePath(request.getCodeFile());
+ mPm.removeCodePath(request.getCodeFile());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index b4ca477ddfec..4ecbd154cab5 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -60,14 +60,10 @@ import java.io.IOException;
*/
final class PackageHandler extends Handler {
private final PackageManagerService mPm;
- private final InstallPackageHelper mInstallPackageHelper;
- private final RemovePackageHelper mRemovePackageHelper;
PackageHandler(Looper looper, PackageManagerService pm) {
super(looper);
mPm = pm;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
- mRemovePackageHelper = new RemovePackageHelper(mPm);
}
@Override
@@ -82,7 +78,7 @@ final class PackageHandler extends Handler {
void doHandleMessage(Message msg) {
switch (msg.what) {
case SEND_PENDING_BROADCAST: {
- mInstallPackageHelper.sendPendingBroadcasts();
+ mPm.sendPendingBroadcasts();
break;
}
case POST_INSTALL: {
@@ -96,7 +92,7 @@ final class PackageHandler extends Handler {
request.onInstallCompleted();
request.runPostInstallRunnable();
if (!request.isInstallExistingForUser()) {
- mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
+ mPm.handlePackagePostInstall(request, didRestore);
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
@@ -107,7 +103,7 @@ final class PackageHandler extends Handler {
case DEFERRED_NO_KILL_POST_DELETE: {
InstallArgs args = (InstallArgs) msg.obj;
if (args != null) {
- mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
+ mPm.cleanUpResources(args.mCodeFile, args.mInstructionSets);
}
} break;
case DEFERRED_NO_KILL_INSTALL_OBSERVER:
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 95b565d235cb..1bb20b4769f5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -18,7 +18,9 @@ package com.android.server.pm;
import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
import static android.os.Process.INVALID_UID;
+
import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -407,11 +409,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) {
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
// Clean up orphaned staging directories
for (File stage : stagingDirsToRemove) {
Slog.w(TAG, "Deleting orphan stage " + stage);
- removePackageHelper.removeCodePath(stage);
+ mPm.removeCodePath(stage);
}
}
@@ -1320,9 +1321,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
@Override
public void installExistingPackage(String packageName, int installFlags, int installReason,
IntentSender statusReceiver, int userId, List<String> allowListedPermissions) {
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
- var result = installPackageHelper.installExistingPackageAsUser(packageName, userId,
+ var result = mPm.installExistingPackageAsUser(packageName, userId,
installFlags, installReason, allowListedPermissions, statusReceiver);
int returnCode = result.first;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 54a2e3adf713..d0e5f96f8d0f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2449,14 +2449,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void onSystemDataLoaderUnrecoverable() {
- final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
final String packageName = getPackageName();
if (TextUtils.isEmpty(packageName)) {
// The package has not been installed.
return;
}
mHandler.post(() -> {
- if (deletePackageHelper.deletePackageX(packageName,
+ if (mPm.deletePackageX(packageName,
PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/)
!= PackageManager.DELETE_SUCCEEDED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index dea66591569f..d69737aef98f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -63,6 +63,7 @@ public class PackageManagerException extends Exception {
public static final int INTERNAL_ERROR_STATIC_SHARED_LIB_OVERLAY_TARGETS = -35;
public static final int INTERNAL_ERROR_APEX_NOT_DIRECTORY = -36;
public static final int INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE = -37;
+ public static final int INTERNAL_ERROR_MISSING_USER = -38;
@IntDef(prefix = { "INTERNAL_ERROR_" }, value = {
INTERNAL_ERROR_NATIVE_LIBRARY_COPY,
@@ -101,7 +102,8 @@ public class PackageManagerException extends Exception {
INTERNAL_ERROR_STATIC_SHARED_LIB_PROTECTED_BROADCAST,
INTERNAL_ERROR_STATIC_SHARED_LIB_OVERLAY_TARGETS,
INTERNAL_ERROR_APEX_NOT_DIRECTORY,
- INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE
+ INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE,
+ INTERNAL_ERROR_MISSING_USER
})
@Retention(RetentionPolicy.SOURCE)
public @interface InternalErrorCode {}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 33cb85c038a0..ddc8369738de 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -70,7 +70,6 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -98,6 +97,7 @@ import android.content.pm.InstantAppInfo;
import android.content.pm.InstantAppRequest;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -115,6 +115,7 @@ import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.PackageLite;
@@ -985,7 +986,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
private final DeletePackageHelper mDeletePackageHelper;
private final InitAppsHelper mInitAppsHelper;
private final AppDataHelper mAppDataHelper;
- private final InstallPackageHelper mInstallPackageHelper;
+ @NonNull private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
@@ -1715,7 +1716,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
(i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
context),
- (i, pm) -> new UpdateOwnershipHelper());
+ (i, pm) -> new UpdateOwnershipHelper(),
+ (i, pm) -> new PackageMonitorCallbackHelper());
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -2067,17 +2069,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
mBroadcastHelper = new BroadcastHelper(mInjector);
- mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(mInjector);
+ mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
mAppDataHelper = new AppDataHelper(this);
- mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
- mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper);
+ mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper, mBroadcastHelper);
+ mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
+ mBroadcastHelper);
+ mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper,
+ mDeletePackageHelper, mBroadcastHelper);
mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
mInjector.getUserManagerInternal(), mDeletePackageHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
- mPreferredActivityHelper = new PreferredActivityHelper(this);
+ mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper);
mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
injector.getCompatibility(), mUserManager, mDomainVerificationManager,
mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity,
@@ -2085,7 +2089,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mDexOptHelper = new DexOptHelper(this);
mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mUserManager,
mBroadcastHelper, mProtectedPackages);
- mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper,
+ mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper,
mSuspendPackageHelper);
mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
mRemovePackageHelper);
@@ -3078,38 +3082,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
- public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
- final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
- final int[] userIds, int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList,
- @Nullable Bundle bOptions) {
- mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags,
- targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
- null /* filterExtrasForReceiver */, bOptions));
- if (targetPkg == null) {
- // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
- // many times to different targets, e.g. installer app, permission controller, other
- // registered apps. We should filter it to avoid calling back many times for the same
- // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
- // installer app or null for registered apps. The callback only need to send back to the
- // registered apps so we check the null condition here.
- notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
- }
- }
-
- void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds,
- int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
- mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
- instantUserIds, broadcastAllowList);
- }
-
- void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
- @NonNull String[] pkgNames, @NonNull int[] uids) {
- mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
- uids);
- }
-
- @Override
public void notifyPackageAdded(String packageName, int uid) {
mPackageObserverHelper.notifyAdded(packageName, uid);
}
@@ -3125,64 +3097,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName);
}
- void sendPackageAddedForUser(@NonNull Computer snapshot, String packageName,
- @NonNull PackageStateInternal packageState, int userId, boolean isArchived,
- int dataLoaderType) {
- final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
- final boolean isSystem = packageState.isSystem();
- final boolean isInstantApp = userState.isInstantApp();
- final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
- final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/,
- false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
- isArchived, dataLoaderType);
-
- // Send a session commit broadcast
- final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
- info.installReason = userState.getInstallReason();
- info.appPackageName = packageName;
- sendSessionCommitBroadcast(info, userId);
- }
-
- @Override
- public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
- boolean sendBootCompleted, boolean includeStopped, @AppIdInt int appId, int[] userIds,
- int[] instantUserIds, boolean isArchived, int dataLoaderType) {
- if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
- return;
- }
- SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot,
- snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
- userIds, snapshot.getPackageStates());
- mHandler.post(
- () -> mBroadcastHelper.sendPackageAddedForNewUsers(packageName, appId, userIds,
- instantUserIds, isArchived, dataLoaderType, broadcastAllowList));
- mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
- instantUserIds, isArchived, dataLoaderType, broadcastAllowList);
- if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
- mHandler.post(() -> {
- for (int userId : userIds) {
- mBroadcastHelper.sendBootCompletedBroadcastToSystemApp(
- packageName, includeStopped, userId);
- }
- }
- );
- }
- }
-
- private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState,
- int userId) {
- final PackageRemovedInfo info = new PackageRemovedInfo(this);
- info.mRemovedPackage = packageName;
- info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName;
- info.mRemovedUsers = new int[] {userId};
- info.mBroadcastUsers = new int[] {userId};
- info.mUid = UserHandle.getUid(userId, packageState.getAppId());
- info.mRemovedPackageVersionCode = packageState.getVersionCode();
- info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/,
- false /*isArchived*/);
- }
-
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
@@ -3505,11 +3419,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- void postPreferredActivityChangedBroadcast(int userId) {
- mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId));
- }
-
-
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
void clearPackagePreferredActivitiesLPw(String packageName,
@@ -3609,17 +3518,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
- UserManagerService ums = UserManagerService.getInstance();
- if (ums == null || sessionInfo.isStaged()) {
- return;
- }
- final UserInfo parent = ums.getProfileParent(userId);
- final int launcherUid = (parent != null) ? parent.id : userId;
- // TODO: Should this snapshot be moved further up?
- final ComponentName launcherComponent = snapshotComputer()
- .getDefaultHomeActivity(launcherUid);
- mBroadcastHelper.sendSessionCommitBroadcast(sessionInfo, userId, launcherUid,
- launcherComponent, mAppPredictionServicePackage);
+ mBroadcastHelper.sendSessionCommitBroadcast(snapshotComputer(), sessionInfo, userId,
+ mAppPredictionServicePackage);
}
private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
@@ -3988,7 +3888,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
+ if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
@@ -4070,8 +3970,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
final int packageUid = UserHandle.getUid(
userId, pkgSettings.get(packageName).getAppId());
- sendPackageChangedBroadcast(newSnapshot, packageName, false /* dontKillApp */,
- components, packageUid, null /* reason */);
+ mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName,
+ false /* dontKillApp */, components, packageUid, null /* reason */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -4147,27 +4047,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- void sendPackageChangedBroadcast(@NonNull Computer snapshot, String packageName,
- boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
- PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
- Process.SYSTEM_UID);
- if (setting == null) {
- return;
- }
- final int userId = UserHandle.getUserId(packageUid);
- final boolean isInstantApp =
- snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
- final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
- final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- final SparseArray<int[]> broadcastAllowList =
- isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
- mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast(
- packageName, dontKillApp, componentNames, packageUid, reason, userIds,
- instantUserIds, broadcastAllowList));
- mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
- packageUid, reason, userIds, instantUserIds, broadcastAllowList);
- }
-
/**
* Used by SystemServer
*/
@@ -4312,7 +4191,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (pkg == null) {
return;
}
- sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
+ mBroadcastHelper.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
true /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
pkg.getUid(),
@@ -5294,7 +5173,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
throw new SecurityException("Calling package " + packageName
+ " does not belong to calling uid " + callingUid);
}
- return mSuspendPackageHelper
+ return SuspendPackageHelper
.getSuspendedPackageAppExtras(snapshot, packageName, userId, callingUid);
}
@@ -5857,10 +5736,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (hidden) {
killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg",
ApplicationExitInfo.REASON_OTHER);
- sendApplicationHiddenForUser(packageName, newPackageState, userId);
+ mBroadcastHelper.sendApplicationHiddenForUser(
+ packageName, newPackageState, userId,
+ /* packageSender= */ PackageManagerService.this);
} else {
- sendPackageAddedForUser(newSnapshot, packageName, newPackageState, userId,
- false /* isArchived */, DataLoaderType.NONE);
+ mBroadcastHelper.sendPackageAddedForUser(
+ newSnapshot, packageName, newPackageState, userId,
+ false /* isArchived */, DataLoaderType.NONE,
+ mAppPredictionServicePackage);
}
scheduleWritePackageRestrictions(userId);
@@ -7929,4 +7812,75 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
}
+
+ void removeCodePath(@Nullable File codePath) {
+ mRemovePackageHelper.removeCodePath(codePath);
+ }
+
+ void cleanUpResources(@Nullable File codeFile, @Nullable String[] instructionSets) {
+ mRemovePackageHelper.cleanUpResources(codeFile, instructionSets);
+ }
+
+ void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) {
+ mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath);
+ }
+
+ void sendPendingBroadcasts() {
+ mInstallPackageHelper.sendPendingBroadcasts();
+ }
+
+ void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) {
+ mInstallPackageHelper.handlePackagePostInstall(request, launchedForRestore);
+ }
+
+ Pair<Integer, IntentSender> installExistingPackageAsUser(
+ @Nullable String packageName,
+ @UserIdInt int userId, @PackageManager.InstallFlags int installFlags,
+ @PackageManager.InstallReason int installReason,
+ @Nullable List<String> allowlistedRestrictedPermissions,
+ @Nullable IntentSender intentSender) {
+ return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+ installReason, allowlistedRestrictedPermissions, intentSender);
+ }
+ AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags)
+ throws PackageManagerException {
+ return mInstallPackageHelper.initPackageTracedLI(scanFile, parseFlags, scanFlags);
+ }
+
+ void restoreDisabledSystemPackageLIF(@NonNull DeletePackageAction action,
+ @NonNull int[] allUserHandles,
+ boolean writeSettings) throws SystemDeleteException {
+ mInstallPackageHelper.restoreDisabledSystemPackageLIF(
+ action, allUserHandles, writeSettings);
+ }
+ boolean enableCompressedPackage(@NonNull AndroidPackage stubPkg,
+ @NonNull PackageSetting stubPs) {
+ return mInstallPackageHelper.enableCompressedPackage(stubPkg, stubPs);
+ }
+
+ void installPackagesTraced(List<InstallRequest> requests) {
+ mInstallPackageHelper.installPackagesTraced(requests);
+ }
+
+ void restoreAndPostInstall(InstallRequest request) {
+ mInstallPackageHelper.restoreAndPostInstall(request);
+ }
+
+ Pair<Integer, String> verifyReplacingVersionCode(@NonNull PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode,
+ int installFlags) {
+ return mInstallPackageHelper.verifyReplacingVersionCode(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
+ int getUidForVerifier(VerifierInfo verifierInfo) {
+ return mInstallPackageHelper.getUidForVerifier(verifierInfo);
+ }
+
+ int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
+ boolean removedBySystem) {
+ return mDeletePackageHelper.deletePackageX(packageName,
+ PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
+ PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 0c2e082e0a86..5b770aab19ca 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -146,6 +146,7 @@ public class PackageManagerServiceInjector {
private final Singleton<SharedLibrariesImpl> mSharedLibrariesProducer;
private final Singleton<CrossProfileIntentFilterHelper> mCrossProfileIntentFilterHelperProducer;
private final Singleton<UpdateOwnershipHelper> mUpdateOwnershipHelperProducer;
+ private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -186,7 +187,8 @@ public class PackageManagerServiceInjector {
Producer<IBackupManager> iBackupManager,
Producer<SharedLibrariesImpl> sharedLibrariesProducer,
Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer,
- Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) {
+ Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer,
+ Producer<PackageMonitorCallbackHelper> packageMonitorCallbackHelper) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -242,6 +244,7 @@ public class PackageManagerServiceInjector {
mCrossProfileIntentFilterHelperProducer = new Singleton<>(
crossProfileIntentFilterHelperProducer);
mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer);
+ mPackageMonitorCallbackHelper = new Singleton<>(packageMonitorCallbackHelper);
}
/**
@@ -431,6 +434,10 @@ public class PackageManagerServiceInjector {
return mUpdateOwnershipHelperProducer.get(this, mPackageManager);
}
+ public PackageMonitorCallbackHelper getPackageMonitorCallbackHelper() {
+ return mPackageMonitorCallbackHelper.get(this, mPackageManager);
+ }
+
/** Provides an abstraction to static access to system state. */
public interface SystemWrapper {
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index bb3bf5360edc..b8c2b8616ea6 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -52,12 +52,6 @@ class PackageMonitorCallbackHelper {
private final Object mLock = new Object();
final IActivityManager mActivityManager = ActivityManager.getService();
- final Handler mHandler;
-
- PackageMonitorCallbackHelper(PackageManagerServiceInjector injector) {
- mHandler = injector.getHandler();
- }
-
@NonNull
@GuardedBy("mLock")
private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
@@ -100,7 +94,8 @@ class PackageMonitorCallbackHelper {
public void notifyPackageAddedForNewUsers(String packageName,
@AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds,
- boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList) {
+ boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList,
+ @NonNull Handler handler) {
Bundle extras = new Bundle(2);
// Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
final int uid = UserHandle.getUid(
@@ -111,11 +106,11 @@ class PackageMonitorCallbackHelper {
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras ,
- userIds /* userIds */, instantUserIds, broadcastAllowList);
+ userIds /* userIds */, instantUserIds, broadcastAllowList, handler);
}
public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
- @NonNull String[] pkgNames, @NonNull int[] uids) {
+ @NonNull String[] pkgNames, @NonNull int[] uids, @NonNull Handler handler) {
Bundle extras = new Bundle();
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
@@ -125,12 +120,12 @@ class PackageMonitorCallbackHelper {
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, handler);
}
public void notifyPackageChanged(String packageName, boolean dontKillApp,
ArrayList<String> componentNames, int packageUid, String reason, int[] userIds,
- int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler) {
Bundle extras = new Bundle(4);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
String[] nameList = new String[componentNames.size()];
@@ -142,11 +137,12 @@ class PackageMonitorCallbackHelper {
extras.putString(Intent.EXTRA_REASON, reason);
}
notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds,
- instantUserIds, broadcastAllowList);
+ instantUserIds, broadcastAllowList, handler);
}
public void notifyPackageMonitor(String action, String pkg, Bundle extras,
- int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
+ Handler handler) {
if (!isAllowedCallbackAction(action)) {
return;
}
@@ -160,9 +156,10 @@ class PackageMonitorCallbackHelper {
}
if (ArrayUtils.isEmpty(instantUserIds)) {
- doNotifyCallbacks(action, pkg, extras, resolvedUserIds, broadcastAllowList);
+ doNotifyCallbacks(
+ action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
} else {
- doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList);
+ doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList, handler);
}
} catch (RemoteException e) {
// do nothing
@@ -181,7 +178,7 @@ class PackageMonitorCallbackHelper {
}
private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds,
- SparseArray<int[]> broadcastAllowList) {
+ SparseArray<int[]> broadcastAllowList, Handler handler) {
RemoteCallbackList<IRemoteCallback> callbacks;
synchronized (mLock) {
callbacks = mCallbacks;
@@ -202,7 +199,7 @@ class PackageMonitorCallbackHelper {
final int[] allowUids =
broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{};
- mHandler.post(() -> callbacks.broadcast((callback, user) -> {
+ handler.post(() -> callbacks.broadcast((callback, user) -> {
RegisterUser registerUser = (RegisterUser) user;
if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
!= userId)) {
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 9f02542a31e4..7ee1772adead 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -16,24 +16,12 @@
package com.android.server.pm;
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
-import android.annotation.NonNull;
-import android.app.ActivityManagerInternal;
-import android.app.BroadcastOptions;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.PowerExemptionManager;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
final class PackageRemovedInfo {
- final PackageSender mPackageSender;
String mRemovedPackage;
String mInstallerPackageName;
int mUid = -1;
@@ -58,116 +46,6 @@ final class PackageRemovedInfo {
InstallArgs mArgs = null;
private static final int[] EMPTY_INT_ARRAY = new int[0];
- PackageRemovedInfo(PackageSender packageSender) {
- mPackageSender = packageSender;
- }
-
- void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem,
- boolean isArchived) {
- sendPackageRemovedBroadcastInternal(killApp, removedBySystem, isArchived);
- }
-
- void sendSystemPackageUpdatedBroadcasts() {
- if (mIsRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal();
- }
- }
-
- private void sendSystemPackageUpdatedBroadcastsInternal() {
- Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
- 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- if (mInstallerPackageName != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- }
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
- mRemovedPackage, null, null, null, null /* broadcastAllowList */,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
- }
-
- private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
- @PowerExemptionManager.ReasonCode int reasonCode) {
- long duration = 10_000;
- final ActivityManagerInternal amInternal =
- LocalServices.getService(ActivityManagerInternal.class);
- if (amInternal != null) {
- duration = amInternal.getBootTimeTempAllowListDuration();
- }
- final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
- bOptions.setTemporaryAppAllowlist(duration,
- TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- reasonCode, "");
- return bOptions;
- }
-
- private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem,
- boolean isArchived) {
- Bundle extras = new Bundle();
- final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid;
- extras.putInt(Intent.EXTRA_UID, removedUid);
- extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
- extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, mIsRemovedPackageSystemUpdate);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
- extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
- final boolean isReplace = mIsUpdate || mIsRemovedPackageSystemUpdate;
- if (isReplace || isArchived) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- if (isArchived) {
- extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
- }
- extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
-
- // Send PACKAGE_REMOVED broadcast to the respective installer.
- if (mRemovedPackage != null && mInstallerPackageName != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, mBroadcastUsers, mInstantUserIds, null, null);
- }
- if (mIsStaticSharedLib) {
- // When uninstalling static shared libraries, only the package's installer needs to be
- // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients.
- return;
- }
- if (mRemovedPackage != null) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
- mRemovedPackage, extras, 0, null /*targetPackage*/, null,
- mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
- mRemovedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
- null /*finishedReceiver*/, mBroadcastUsers, mInstantUserIds,
- mBroadcastAllowList, null /*bOptions*/);
- if (mDataRemoved && !mIsRemovedPackageSystemUpdate) {
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
- mRemovedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
- null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
- mPackageSender.notifyPackageRemoved(mRemovedPackage, removedUid);
- }
- }
- if (mRemovedAppId >= 0) {
- // If a system app's updates are uninstalled the UID is not actually removed. Some
- // services need to know the package name affected.
- if (isReplace) {
- extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
- }
-
- mPackageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
- null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
- null, null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
- }
- }
-
public void populateBroadcastUsers(PackageSetting deletedPackageSetting) {
if (mRemovedUsers == null) {
mBroadcastUsers = null;
diff --git a/services/core/java/com/android/server/pm/PackageSender.java b/services/core/java/com/android/server/pm/PackageSender.java
index 82e1d5f389c5..db83f593d5ee 100644
--- a/services/core/java/com/android/server/pm/PackageSender.java
+++ b/services/core/java/com/android/server/pm/PackageSender.java
@@ -16,24 +16,7 @@
package com.android.server.pm;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IIntentReceiver;
-import android.os.Bundle;
-import android.util.SparseArray;
-
interface PackageSender {
- /**
- * @param userIds User IDs where the action occurred on a full application
- * @param instantUserIds User IDs where the action occurred on an instant application
- */
- void sendPackageBroadcast(String action, String pkg,
- Bundle extras, int flags, String targetPkg,
- IIntentReceiver finishedReceiver, int[] userIds, int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions);
- void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
- boolean sendBootCompleted, boolean includeStopped, int appId, int[] userIds,
- int[] instantUserIds, boolean isArchived, int dataLoaderType);
void notifyPackageAdded(String packageName, int uid);
void notifyPackageChanged(String packageName, int uid);
void notifyPackageRemoved(String packageName, int uid);
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 571aab4f6969..41d2aeb9b168 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -69,10 +69,12 @@ final class PreferredActivityHelper {
private static final String TAG_DEFAULT_APPS = "da";
private final PackageManagerService mPm;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
- PreferredActivityHelper(PackageManagerService pm) {
+ PreferredActivityHelper(PackageManagerService pm, BroadcastHelper broadcastHelper) {
mPm = pm;
+ mBroadcastHelper = broadcastHelper;
}
private ResolveInfo findPreferredActivityNotLocked(@NonNull Computer snapshot, Intent intent,
@@ -120,7 +122,7 @@ final class PreferredActivityHelper {
}
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), changedUsers);
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -167,7 +169,7 @@ final class PreferredActivityHelper {
return mPm.setActiveLauncherPackage(packageName, userId,
successful -> {
if (successful) {
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
});
}
@@ -215,7 +217,7 @@ final class PreferredActivityHelper {
}
// Re-snapshot after mLock
if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId))) {
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
}
@@ -411,7 +413,7 @@ final class PreferredActivityHelper {
if (isHomeFilter(filter)) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
}
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
@@ -426,7 +428,7 @@ final class PreferredActivityHelper {
}
if (changed) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -443,7 +445,7 @@ final class PreferredActivityHelper {
}
if (changed) {
updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
mPm.scheduleWritePackageRestrictions(userId);
}
}
@@ -616,7 +618,7 @@ final class PreferredActivityHelper {
mPm.clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
}
if (changedUsers.size() > 0) {
- mPm.postPreferredActivityChangedBroadcast(userId);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
}
synchronized (mPm.mLock) {
mPm.mSettings.applyDefaultPreferredAppsLPw(userId);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index d989c90a0597..b055a3ffd688 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -22,6 +22,7 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
@@ -69,9 +70,11 @@ final class RemovePackageHelper {
private final PermissionManagerServiceInternal mPermissionManager;
private final SharedLibrariesImpl mSharedLibraries;
private final AppDataHelper mAppDataHelper;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
- RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+ RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper,
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mIncrementalManager = mPm.mInjector.getIncrementalManager();
mInstaller = mPm.mInjector.getInstaller();
@@ -79,10 +82,7 @@ final class RemovePackageHelper {
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl();
mAppDataHelper = appDataHelper;
- }
-
- RemovePackageHelper(PackageManagerService pm) {
- this(pm, new AppDataHelper(pm));
+ mBroadcastHelper = broadcastHelper;
}
public void removeCodePath(File codePath) {
@@ -265,7 +265,8 @@ final class RemovePackageHelper {
final List<AndroidPackage> sharedUserPkgs =
sus != null ? sus.getPackages() : Collections.emptyList();
- final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
+ final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm,
+ mBroadcastHelper);
final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
: new int[] {userId};
for (int nextUserId : userIds) {
@@ -395,13 +396,13 @@ final class RemovePackageHelper {
}
if (changedUsers.size() > 0) {
final PreferredActivityHelper preferredActivityHelper =
- new PreferredActivityHelper(mPm);
+ new PreferredActivityHelper(mPm, mBroadcastHelper);
preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(),
changedUsers);
- mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
+ mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
}
} else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate
- && outInfo.mRemovedUsers != null) {
+ && outInfo.mRemovedUsers != null && !outInfo.mIsExternal) {
// For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false
// for affected users. This does not apply to app updates where the old apk is replaced
// but the old data remains.
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 0ea45c466ca7..e993d9e5b724 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -372,6 +372,7 @@ class ShortcutPackage extends ShortcutPackageItem {
// Extract Icon and update the icon res ID and the bitmap path.
s.saveIconAndFixUpShortcutLocked(this, newShortcut);
s.fixUpShortcutResourceNamesAndValues(newShortcut);
+ ensureShortcutCountBeforePush();
saveShortcut(newShortcut);
}
@@ -426,7 +427,6 @@ class ShortcutPackage extends ShortcutPackageItem {
@NonNull List<ShortcutInfo> changedShortcuts) {
Preconditions.checkArgument(newShortcut.isEnabled(),
"pushDynamicShortcuts() cannot publish disabled shortcuts");
- ensureShortcutCountBeforePush();
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index db5b9b199b85..c725cdca9d76 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -147,7 +147,6 @@ public final class StorageEventHelper extends StorageEventListener {
final Settings.VersionInfo ver;
final List<? extends PackageStateInternal> packages;
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
synchronized (mPm.mLock) {
ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
@@ -160,7 +159,7 @@ public final class StorageEventHelper extends StorageEventListener {
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
- pkg = installPackageHelper.initPackageTracedLI(
+ pkg = mPm.initPackageTracedLI(
ps.getPath(), parseFlags, SCAN_INITIAL);
loaded.add(pkg);
@@ -228,7 +227,8 @@ public final class StorageEventHelper extends StorageEventListener {
}
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
- sendResourcesChangedBroadcast(true /* mediaStatus */, false /* replacing */, loaded);
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ true /* mediaStatus */, false /* replacing */, loaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.add(vol.getId());
}
@@ -256,7 +256,7 @@ public final class StorageEventHelper extends StorageEventListener {
final AndroidPackage pkg = ps.getPkg();
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);
+ final PackageRemovedInfo outInfo = new PackageRemovedInfo();
try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(),
UserHandle.USER_ALL, deleteFlags,
@@ -280,7 +280,8 @@ public final class StorageEventHelper extends StorageEventListener {
}
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
- sendResourcesChangedBroadcast(false /* mediaStatus */, false /* replacing */, unloaded);
+ mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+ false /* mediaStatus */, false /* replacing */, unloaded);
synchronized (mLoadedVolumes) {
mLoadedVolumes.remove(vol.getId());
}
@@ -295,21 +296,6 @@ public final class StorageEventHelper extends StorageEventListener {
}
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<AndroidPackage> packages) {
- final int size = packages.size();
- final String[] packageNames = new String[size];
- final int[] packageUids = new int[size];
- for (int i = 0; i < size; i++) {
- final AndroidPackage pkg = packages.get(i);
- packageNames[i] = pkg.getPackageName();
- packageUids[i] = pkg.getUid();
- }
- mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, mediaStatus,
- replacing, packageNames, packageUids);
- mPm.notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
- }
-
/**
* Examine all apps present on given mounted volume, and destroy apps that
* aren't expected, either due to uninstallation or reinstallation on
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index ddb045df4eaf..29d99a73a034 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -26,17 +26,13 @@ import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.SuspendDialogInfo;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
@@ -47,7 +43,6 @@ import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
@@ -207,19 +202,22 @@ public final class SuspendPackageHelper {
}
});
+ final Computer newSnapshot = mPm.snapshotComputer();
if (!notifyPackagesList.isEmpty()) {
final String[] changedPackages =
notifyPackagesList.toArray(new String[0]);
- sendPackagesSuspendedForUser(
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
changedPackages, notifyUids.toArray(), quarantined, userId);
- sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
+ suspended, userId);
mPm.scheduleWritePackageRestrictions(userId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
if (!changedPackagesList.isEmpty()) {
- sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
userId);
}
@@ -269,8 +267,10 @@ public final class SuspendPackageHelper {
* @return The app extras of the suspended package.
*/
@Nullable
- Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot, @NonNull String packageName,
- int userId, int callingUid) {
+ static Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot,
+ @NonNull String packageName,
+ int userId,
+ int callingUid) {
final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid);
if (ps == null) {
return null;
@@ -299,7 +299,7 @@ public final class SuspendPackageHelper {
* suspensions will be removed.
* @param userId The user for which the changes are taking place.
*/
- void removeSuspensionsBySuspendingPackage(@NonNull Computer computer,
+ void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
@NonNull String[] packagesToChange,
@NonNull Predicate<String> suspendingPackagePredicate, int userId) {
final List<String> unsuspendedPackages = new ArrayList<>();
@@ -307,7 +307,7 @@ public final class SuspendPackageHelper {
final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
for (String packageName : packagesToChange) {
final PackageStateInternal packageState =
- computer.getPackageStateInternal(packageName);
+ snapshot.getPackageStateInternal(packageName);
final PackageUserStateInternal packageUserState = packageState == null
? null : packageState.getUserStateOrDefault(userId);
if (packageUserState == null || !packageUserState.isSuspended()) {
@@ -350,11 +350,14 @@ public final class SuspendPackageHelper {
});
mPm.scheduleWritePackageRestrictions(userId);
+ final Computer newSnapshot = mPm.snapshotComputer();
if (!unsuspendedPackages.isEmpty()) {
final String[] packageArray = unsuspendedPackages.toArray(
new String[unsuspendedPackages.size()]);
- sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
- sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+ mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
+ false, userId);
+ mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+ Intent.ACTION_PACKAGES_UNSUSPENDED,
packageArray, unsuspendedUids.toArray(), false, userId);
}
}
@@ -610,38 +613,6 @@ public final class SuspendPackageHelper {
}
/**
- * Send broadcast intents for packages suspension changes.
- *
- * @param intent The action name of the suspension intent.
- * @param pkgList The names of packages which have suspension changes.
- * @param uidList The uids of packages which have suspension changes.
- * @param userId The user where packages reside.
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
- @NonNull int[] uidList, boolean quarantined, int userId) {
- final Handler handler = mInjector.getHandler();
- final Bundle extras = new Bundle(3);
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- if (quarantined) {
- extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
- }
- final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
- final Bundle options = new BroadcastOptions()
- .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
- .toBundle();
- handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
- extras, flags, null /* targetPkg */, null /* finishedReceiver */,
- new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
- (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
- mPm.snapshotComputer(), callingUid, intentExtras),
- options));
- mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
- null /* instantUserIds */, null /* broadcastAllowList */);
- }
-
- /**
* Suspends packages on behalf of an admin.
*
* @return array of packages that are unsuspendable, either because admin is not allowed to
@@ -756,37 +727,4 @@ public final class SuspendPackageHelper {
}
return false;
}
-
- private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
- int userId) {
- final Handler handler = mInjector.getHandler();
- final String action = suspended
- ? Intent.ACTION_MY_PACKAGE_SUSPENDED
- : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
- handler.post(() -> {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
- + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
- return;
- }
- final int[] targetUserIds = new int[] {userId};
- final Computer snapshot = mPm.snapshotComputer();
- for (String packageName : affectedPackages) {
- final Bundle appExtras = suspended
- ? getSuspendedPackageAppExtras(snapshot, packageName, userId, SYSTEM_UID)
- : null;
- final Bundle intentExtras;
- if (appExtras != null) {
- intentExtras = new Bundle(1);
- intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
- } else {
- intentExtras = null;
- }
- mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false, null, null, null);
- }
- });
- }
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 04d1da61f3cc..390c45b6a524 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -145,6 +145,19 @@
"include-filter": "android.content.pm.cts.PackageManagerShellCommandMultiUserTest"
}
]
+ },
+ {
+ "file_patterns": [
+ "(/|^)InstallPackageHelper\\.java",
+ "services/core/java/com/android/server/pm/parsing/.*",
+ "services/core/java/com/android/server/pm/pkg/parsing/.*"
+ ],
+ "name": "SdkSandboxManagerServiceUnitTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"imports": [
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 8c73ce8b6b95..c6435aeaba4d 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -139,7 +139,6 @@ final class VerifyingSession {
private final UserHandle mUser;
@NonNull
private final PackageManagerService mPm;
- private final InstallPackageHelper mInstallPackageHelper;
VerifyingSession(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
@@ -147,7 +146,6 @@ final class VerifyingSession {
boolean userActionRequired, PackageManagerService pm) {
mPm = pm;
mUser = user;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
mObserver = observer;
mInstallFlags = sessionParams.installFlags;
@@ -181,7 +179,7 @@ final class VerifyingSession {
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
- Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
+ Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
setReturnCode(ret.first, ret.second);
if (mRet != INSTALL_SUCCEEDED) {
@@ -729,7 +727,7 @@ final class VerifyingSession {
continue;
}
- final int verifierUid = mInstallPackageHelper.getUidForVerifier(verifierInfo);
+ final int verifierUid = mPm.getUidForVerifier(verifierInfo);
if (verifierUid == -1) {
continue;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dc75a98c5bdc..097656cac7f7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1983,7 +1983,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void run() {
if (mPendingHomeKeyEvent != null) {
handleShortPressOnHome(mPendingHomeKeyEvent);
- mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
}
}
@@ -2028,7 +2027,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
|| mPictureInPictureVisible) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
- mPendingHomeKeyEvent = KeyEvent.obtain(event);
+ mPendingHomeKeyEvent = event;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return true;
@@ -2036,11 +2035,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
// Post to main thread to avoid blocking input pipeline.
- final KeyEvent shortPressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleShortPressOnHome(shortPressEvent);
- shortPressEvent.recycle();
- });
+ mHandler.post(() -> handleShortPressOnHome(event));
return true;
}
@@ -2067,14 +2062,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (repeatCount == 0) {
mHomePressed = true;
if (mPendingHomeKeyEvent != null) {
- mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- final KeyEvent doublePressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleDoubleTapOnHome(doublePressEvent);
- doublePressEvent.recycle();
- });
+ mHandler.post(() -> handleDoubleTapOnHome(event));
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -2084,11 +2074,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
// Post to main thread to avoid blocking input pipeline.
- final KeyEvent longPressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleLongPressOnHome(longPressEvent);
- longPressEvent.recycle();
- });
+ mHandler.post(() -> handleLongPressOnHome(event));
}
}
return true;
diff --git a/services/core/java/com/android/server/security/rkp/OWNERS b/services/core/java/com/android/server/security/rkp/OWNERS
index 348f94048311..ea6dc727c7b2 100644
--- a/services/core/java/com/android/server/security/rkp/OWNERS
+++ b/services/core/java/com/android/server/security/rkp/OWNERS
@@ -1 +1 @@
-file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
+file:platform/frameworks/base:main:/core/java/android/security/rkp/OWNERS
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 96f4a01f7f3a..c2666f63d7a6 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -297,7 +297,8 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn
return;
}
- for (ClientState clientState : mClients.values()) {
+
+ for (ClientState clientState : mClients.values().toArray(new ClientState[0])) {
tryRespondWithError(
clientState.mDelegatingListener.mRemoteListener,
SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 40e9c1305f01..88eaafaee370 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -955,6 +955,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
+ public void setTiles(String tiles) {
+ enforceStatusBarOrShell();
+
+ if (mBar != null) {
+ try {
+ mBar.setQsTiles(tiles.split(","));
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
public void clickTile(ComponentName component) {
enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index 11a4976d945f..d6bf02fcdc47 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -61,6 +61,8 @@ public class StatusBarShellCommand extends ShellCommand {
return runAddTile();
case "remove-tile":
return runRemoveTile();
+ case "set-tiles":
+ return runSetTiles();
case "click-tile":
return runClickTile();
case "check-support":
@@ -105,6 +107,11 @@ public class StatusBarShellCommand extends ShellCommand {
return 0;
}
+ private int runSetTiles() throws RemoteException {
+ mInterface.setTiles(getNextArgRequired());
+ return 0;
+ }
+
private int runClickTile() throws RemoteException {
mInterface.clickTile(ComponentName.unflattenFromString(getNextArgRequired()));
return 0;
@@ -242,6 +249,9 @@ public class StatusBarShellCommand extends ShellCommand {
pw.println(" remove-tile COMPONENT");
pw.println(" Remove a TileService of the specified component");
pw.println("");
+ pw.println(" set-tiles LIST-OF-TILES");
+ pw.println(" Sets the list of tiles as the current Quick Settings tiles");
+ pw.println("");
pw.println(" click-tile COMPONENT");
pw.println(" Click on a TileService of the specified component");
pw.println("");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a0c78702fec5..ed10346d8495 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -580,7 +580,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
IBinder mRequestedLaunchingTaskFragmentToken;
// Tracking splash screen status from previous activity
- boolean mAllowIconSplashScreen = true;
+ boolean mSplashScreenStyleSolidColor = false;
boolean mPauseSchedulePendingForPip = false;
@@ -2408,7 +2408,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@VisibleForTesting
boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,
boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,
- boolean activityCreated, boolean allowIcon, boolean activityAllDrawn) {
+ boolean activityCreated, boolean isSimple,
+ boolean activityAllDrawn) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
if (!okToDisplay()) {
@@ -2443,8 +2444,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int typeParameter = StartingSurfaceController
.makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, allowIcon, useLegacy,
- activityAllDrawn, type, packageName, mUserId);
+ allowTaskSnapshot, activityCreated, isSimple, useLegacy, activityAllDrawn,
+ type, packageName, mUserId);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
if (isActivityTypeHome()) {
@@ -5365,7 +5366,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Finish should only ever commit visibility=false, so we can check full containment
// rather than just direct membership.
inFinishingTransition = mTransitionController.inFinishingTransition(this);
- if (!inFinishingTransition && !mDisplayContent.isSleeping()) {
+ if (!inFinishingTransition && (visible || !mDisplayContent.isSleeping())) {
Slog.e(TAG, "setVisibility=" + visible
+ " while transition is not collecting or finishing "
+ this + " caller=" + Debug.getCallers(8));
@@ -6746,7 +6747,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// stop tracking
- mAllowIconSplashScreen = false;
+ mSplashScreenStyleSolidColor = true;
if (mStartingWindow != null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
@@ -6795,7 +6796,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void onStartingWindowDrawn() {
boolean wasTaskVisible = false;
if (task != null) {
- mAllowIconSplashScreen = false;
+ mSplashScreenStyleSolidColor = true;
wasTaskVisible = !setTaskHasBeenVisible();
}
@@ -7320,32 +7321,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Checks whether an icon splash screen can be used in the starting window based on the
- * preference in the {@code options} and this activity's theme, giving higher priority to the
- * {@code options}'s preference.
- *
- * When no preference is specified, a default behaviour is defined:
- * - if the activity is started from the home or shell app, an icon can be used
- * - if the activity is started from SystemUI, an icon should not be used
- * - if there is a launching activity, use its preference
- * - if none of the above is met, only use an icon when the activity is started for the first
- * time from a System app
- *
- * The returned value is sent to WmShell, which will make the final decision on what splash
- * screen type will be used.
- *
- * @return true if an icon can be used in the splash screen
- * false when an icon should not be used in the splash screen
+ * @return true if a solid color splash screen must be used
+ * false when an icon splash screen can be used, but the final decision for whether to
+ * use an icon or solid color splash screen will be made by WmShell.
*/
- private boolean canUseIconSplashScreen(ActivityRecord sourceRecord,
+ private boolean shouldUseSolidColorSplashScreen(ActivityRecord sourceRecord,
boolean startActivity, ActivityOptions options, int resolvedTheme) {
if (sourceRecord == null && !startActivity) {
- // Shouldn't use an icon if this activity is not top activity. This could happen when
- // adding a splash screen window to the warm start activity which is re-create because
- // top is finishing.
+ // Use simple style if this activity is not top activity. This could happen when adding
+ // a splash screen window to the warm start activity which is re-create because top is
+ // finishing.
final ActivityRecord above = task.getActivityAbove(this);
if (above != null) {
- return false;
+ return true;
}
}
@@ -7353,33 +7341,32 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int optionsStyle = options != null ? options.getSplashScreenStyle() :
SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR) {
- return false;
+ return true;
} else if (optionsStyle == SplashScreen.SPLASH_SCREEN_STYLE_ICON
|| isIconStylePreferred(resolvedTheme)) {
- return true;
+ return false;
}
// Choose the default behavior when neither the ActivityRecord nor the activity theme have
// specified a splash screen style.
if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME || launchedFromUid == Process.SHELL_UID) {
- return true;
- } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
return false;
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ return true;
} else {
- // Need to check sourceRecord in case this activity is launched from a service or a
- // trampoline activity.
+ // Need to check sourceRecord in case this activity is launched from a service.
if (sourceRecord == null) {
sourceRecord = searchCandidateLaunchingActivity();
}
if (sourceRecord != null) {
- return sourceRecord.mAllowIconSplashScreen;
+ return sourceRecord.mSplashScreenStyleSolidColor;
}
// Use an icon if the activity was launched from System for the first start.
- // Otherwise, can't use an icon splash screen.
- return mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEM && startActivity;
+ // Otherwise, must use solid color splash screen.
+ return mLaunchSourceType != LAUNCH_SOURCE_TYPE_SYSTEM || !startActivity;
}
}
@@ -7443,7 +7430,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
splashScreenTheme);
- mAllowIconSplashScreen = canUseIconSplashScreen(sourceRecord, startActivity,
+ mSplashScreenStyleSolidColor = shouldUseSolidColorSplashScreen(sourceRecord, startActivity,
startOptions, resolvedTheme);
final boolean activityCreated =
@@ -7455,7 +7442,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
prev, newTask || newSingleActivity, taskSwitch, processRunning,
- allowTaskSnapshot(), activityCreated, mAllowIconSplashScreen, allDrawn);
+ allowTaskSnapshot(), activityCreated, mSplashScreenStyleSolidColor, allDrawn);
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
Slog.d(TAG, "Scheduled starting window for " + this);
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index d461d1ec02f0..a1b8949c2582 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -130,6 +130,7 @@ public class DisplayRotation {
private final int mUndockedHdmiRotation;
private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
private final RotationHistory mRotationHistory = new RotationHistory();
+ private final RotationLockHistory mRotationLockHistory = new RotationLockHistory();
private OrientationListener mOrientationListener;
private StatusBarManagerInternal mStatusBarManagerInternal;
@@ -922,7 +923,8 @@ public class DisplayRotation {
}
@VisibleForTesting
- void setUserRotation(int userRotationMode, int userRotation) {
+ void setUserRotation(int userRotationMode, int userRotation, String caller) {
+ mRotationLockHistory.addRecord(userRotationMode, userRotation, caller);
mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
if (useDefaultSettingsProvider()) {
// We'll be notified via settings listener, so we don't need to update internal values.
@@ -953,17 +955,17 @@ public class DisplayRotation {
}
}
- void freezeRotation(int rotation) {
+ void freezeRotation(int rotation, String caller) {
if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) {
rotation = RotationUtils.reverseRotationDirectionAroundZAxis(rotation);
}
rotation = (rotation == -1) ? mRotation : rotation;
- setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
+ setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation, caller);
}
- void thawRotation() {
- setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
+ void thawRotation(String caller) {
+ setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation, caller);
}
boolean isRotationFrozen() {
@@ -1712,6 +1714,15 @@ public class DisplayRotation {
r.dump(prefix, pw);
}
}
+
+ if (!mRotationLockHistory.mRecords.isEmpty()) {
+ pw.println();
+ pw.println(prefix + " RotationLockHistory");
+ prefix = " " + prefix;
+ for (RotationLockHistory.Record r : mRotationLockHistory.mRecords) {
+ r.dump(prefix, pw);
+ }
+ }
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -2133,6 +2144,40 @@ public class DisplayRotation {
}
}
+ private static class RotationLockHistory {
+ private static final int MAX_SIZE = 8;
+
+ private static class Record {
+ @WindowManagerPolicy.UserRotationMode final int mUserRotationMode;
+ @Surface.Rotation final int mUserRotation;
+ final String mCaller;
+ final long mTimestamp = System.currentTimeMillis();
+
+ private Record(int userRotationMode, int userRotation, String caller) {
+ mUserRotationMode = userRotationMode;
+ mUserRotation = userRotation;
+ mCaller = caller;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + TimeUtils.logTimeOfDay(mTimestamp) + ": "
+ + "mode=" + WindowManagerPolicy.userRotationModeToString(mUserRotationMode)
+ + ", rotation=" + Surface.rotationToString(mUserRotation)
+ + ", caller=" + mCaller);
+ }
+ }
+
+ private final ArrayDeque<RotationLockHistory.Record> mRecords = new ArrayDeque<>(MAX_SIZE);
+
+ void addRecord(@WindowManagerPolicy.UserRotationMode int userRotationMode,
+ @Surface.Rotation int userRotation, String caller) {
+ if (mRecords.size() >= MAX_SIZE) {
+ mRecords.removeFirst();
+ }
+ mRecords.addLast(new Record(userRotationMode, userRotation, caller));
+ }
+ }
+
private static class RotationHistory {
private static final int MAX_SIZE = 8;
private static final int NO_FOLD_CONTROLLER = -2;
diff --git a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
index d3a8a82f8f87..4eb2d88cb4eb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
@@ -118,7 +118,8 @@ final class DisplayRotationReversionController {
if (mDisplayContent.getDisplayRotation().isRotationFrozen()) {
mDisplayContent.getDisplayRotation().setUserRotation(
mUserRotationModeOverridden,
- mUserRotationOverridden);
+ mUserRotationOverridden,
+ /* caller= */ "DisplayRotationReversionController#revertOverride");
return true;
} else {
return false;
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f33ecaa90531..442269ab4015 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -64,6 +64,7 @@ import android.graphics.Bitmap;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -301,9 +302,12 @@ class RecentTasks {
}
// Always update the reordering time when this is called to ensure that the timeout
- // is reset
+ // is reset. Extend this duration when running in tests.
+ final long timeout = ActivityManager.isRunningInUserTestHarness()
+ ? mFreezeTaskListTimeoutMs * 10
+ : mFreezeTaskListTimeoutMs;
mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
- mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
+ mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, timeout);
}
/**
@@ -503,6 +507,16 @@ class RecentTasks {
Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
List<Task> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks);
+
+ // Tasks are ordered from most recent to least recent. Update the last active time to be
+ // in sync with task recency when device reboots, so the most recent task has the
+ // highest last active time
+ long currentElapsedTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < tasks.size(); i++) {
+ Task task = tasks.get(i);
+ task.lastActiveTime = currentElapsedTime - i;
+ }
+
mTasks.addAll(tasks);
cleanupLocked(userId);
mUsersWithRecentsLoaded.put(userId, true);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a0517bebca6d..a55c232990cf 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -19,12 +19,12 @@ package com.android.server.wm;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
-import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -102,7 +102,7 @@ public class StartingSurfaceController {
static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
- boolean allowIcon, boolean useLegacy, boolean activityDrawn, int startingWindowType,
+ boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType,
String packageName, int userId) {
int parameter = 0;
if (newTask) {
@@ -120,8 +120,8 @@ public class StartingSurfaceController {
if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) {
parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
}
- if (allowIcon) {
- parameter |= TYPE_PARAMETER_ALLOW_ICON;
+ if (isSolidColor) {
+ parameter |= TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
}
if (useLegacy) {
parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9a1920d7a217..3faf8b9ff8a8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4281,8 +4281,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void freezeRotation(int rotation) {
- freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
+ public void freezeRotation(int rotation, String caller) {
+ freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation, caller);
}
/**
@@ -4292,7 +4292,7 @@ public class WindowManagerService extends IWindowManager.Stub
* @param rotation The desired rotation to freeze to, or -1 to use the current rotation.
*/
@Override
- public void freezeDisplayRotation(int displayId, int rotation) {
+ public void freezeDisplayRotation(int displayId, int rotation, String caller) {
// TODO(multi-display): Track which display is rotated.
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"freezeRotation()")) {
@@ -4302,6 +4302,9 @@ public class WindowManagerService extends IWindowManager.Stub
throw new IllegalArgumentException("Rotation argument must be -1 or a valid "
+ "rotation constant.");
}
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s",
+ getDefaultDisplayRotation(), rotation, caller);
final long origId = Binder.clearCallingIdentity();
try {
@@ -4311,7 +4314,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Trying to freeze rotation for a missing display.");
return;
}
- display.getDisplayRotation().freezeRotation(rotation);
+ display.getDisplayRotation().freezeRotation(rotation, caller);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -4321,8 +4324,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void thawRotation() {
- thawDisplayRotation(Display.DEFAULT_DISPLAY);
+ public void thawRotation(String caller) {
+ thawDisplayRotation(Display.DEFAULT_DISPLAY, caller);
}
/**
@@ -4330,13 +4333,14 @@ public class WindowManagerService extends IWindowManager.Stub
* Persists across reboots.
*/
@Override
- public void thawDisplayRotation(int displayId) {
+ public void thawDisplayRotation(int displayId, String caller) {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"thawRotation()")) {
throw new SecurityException("Requires SET_ORIENTATION permission");
}
- ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation());
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d, caller=%s",
+ getDefaultDisplayRotation(), caller);
final long origId = Binder.clearCallingIdentity();
try {
@@ -4346,7 +4350,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Trying to thaw rotation for a missing display.");
return;
}
- display.getDisplayRotation().thawRotation();
+ display.getDisplayRotation().thawRotation(caller);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -5237,16 +5241,17 @@ public class WindowManagerService extends IWindowManager.Stub
applyForcedPropertiesForDefaultDisplay();
mAnimator.ready();
mDisplayReady = true;
- // Reconfigure all displays to make sure that forced properties and
- // DisplayWindowSettings are applied.
- mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
+ mHasWideColorGamutSupport = queryWideColorGamutSupport();
+ mHasHdrSupport = queryHdrSupport();
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_FAKETOUCH);
+ // Reconfigure all displays to make sure that the forced properties and
+ // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also
+ // affect the Configuration.
+ mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
}
-
- mAtmService.updateConfiguration(null /* request to compute config */);
}
public void systemReady() {
@@ -5254,8 +5259,6 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.systemReady();
mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
mSnapshotController.systemReady();
- mHasWideColorGamutSupport = queryWideColorGamutSupport();
- mHasHdrSupport = queryHdrSupport();
UiThread.getHandler().post(mSettingsObserver::loadSettings);
IVrManager vrManager = IVrManager.Stub.asInterface(
ServiceManager.getService(Context.VR_SERVICE));
@@ -9643,4 +9646,15 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
}
}
+
+ /**
+ * Resets the spatial ordering of recents for testing purposes.
+ */
+ void resetFreezeRecentTaskListReordering() {
+ if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS,
+ "resetFreezeRecentTaskListReordering()")) {
+ throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
+ }
+ mAtmService.getRecentTasks().resetFreezeTaskListReorderingOnTimeout();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 74a0bafd3a4c..fa9a65fda853 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -142,6 +142,8 @@ public class WindowManagerShellCommand extends ShellCommand {
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
+ case "reset-freeze-recent-tasks":
+ return runResetFreezeRecentTaskListReordering(pw);
case "shell":
return runWmShellCommand(pw);
default:
@@ -252,6 +254,11 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runResetFreezeRecentTaskListReordering(PrintWriter pw) throws RemoteException {
+ mInternal.resetFreezeRecentTaskListReordering();
+ return 0;
+ }
+
private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
try {
final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
@@ -438,7 +445,8 @@ public class WindowManagerShellCommand extends ShellCommand {
}
if ("free".equals(lockMode)) {
- mInternal.thawDisplayRotation(displayId);
+ mInternal.thawDisplayRotation(displayId,
+ /* caller= */ "WindowManagerShellCommand#free");
return 0;
}
@@ -451,7 +459,8 @@ public class WindowManagerShellCommand extends ShellCommand {
try {
final int rotation =
arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */;
- mInternal.freezeDisplayRotation(displayId, rotation);
+ mInternal.freezeDisplayRotation(displayId, rotation,
+ /* caller= */ "WindowManagerShellCommand#lock");
return 0;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println("Error: " + e.getMessage());
@@ -1433,7 +1442,8 @@ public class WindowManagerShellCommand extends ShellCommand {
mInterface.setForcedDisplayScalingMode(displayId, DisplayContent.FORCE_SCALING_MODE_AUTO);
// user-rotation
- mInternal.thawDisplayRotation(displayId);
+ mInternal.thawDisplayRotation(displayId,
+ /* caller= */ "WindowManagerShellCommand#runReset");
// fixed-to-user-rotation
mInterface.setFixedToUserRotation(displayId, IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT);
@@ -1489,6 +1499,8 @@ public class WindowManagerShellCommand extends ShellCommand {
printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
+ pw.println(" reset-freeze-recent-tasks");
+ pw.println(" Resets the spatial ordering of the recent tasks list");
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index d0ead141b58c..25e8475fcf42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -21,6 +21,7 @@ import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_TARGET_USER_ID
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_UPDATE_RESULT_KEY;
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_CONFLICTING_ADMIN_POLICY;
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_HARDWARE_LIMITATION;
+import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_STORAGE_LIMIT_REACHED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET;
import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;
@@ -51,6 +52,7 @@ import android.content.pm.UserProperties;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -63,6 +65,7 @@ import android.util.Xml;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.devicepolicy.flags.FlagUtils;
import com.android.server.utils.Slogf;
import libcore.io.IoUtils;
@@ -117,6 +120,10 @@ final class DevicePolicyEngine {
* Map containing the current set of admins in each user with active policies.
*/
private final SparseArray<Set<EnforcingAdmin>> mEnforcingAdmins;
+ private final SparseArray<HashMap<EnforcingAdmin, Integer>> mAdminPolicySize;
+
+ //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
+ private static final int POLICY_SIZE_LIMIT = 99999;
private final DeviceAdminServiceController mDeviceAdminServiceController;
@@ -131,6 +138,7 @@ final class DevicePolicyEngine {
mLocalPolicies = new SparseArray<>();
mGlobalPolicies = new HashMap<>();
mEnforcingAdmins = new SparseArray<>();
+ mAdminPolicySize = new SparseArray<>();
}
/**
@@ -139,7 +147,6 @@ final class DevicePolicyEngine {
*
* <p>If {@code skipEnforcePolicy} is true, it sets the policies in the internal data structure
* but doesn't call the enforcing logic.
- *
*/
<V> void setLocalPolicy(
@NonNull PolicyDefinition<V> policyDefinition,
@@ -152,6 +159,12 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+ policyDefinition, userId)) {
+ return;
+ }
+ }
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -236,6 +249,7 @@ final class DevicePolicyEngine {
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Set the policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
@@ -250,6 +264,7 @@ final class DevicePolicyEngine {
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Removes any previously set policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin}.
@@ -267,6 +282,10 @@ final class DevicePolicyEngine {
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
+ }
+
if (policyDefinition.isNonCoexistablePolicy()) {
setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
enforcingAdmin, /* value= */ null, userId, /* skipEnforcePolicy= */ false);
@@ -392,6 +411,7 @@ final class DevicePolicyEngine {
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Set the policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
@@ -407,6 +427,13 @@ final class DevicePolicyEngine {
Objects.requireNonNull(value);
synchronized (mLock) {
+ PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
+ policyDefinition, UserHandle.USER_ALL)) {
+ return;
+ }
+ }
// TODO(b/270999567): Move error handling for DISALLOW_CELLULAR_2G into the code
// that honors the restriction once there's an API available
if (checkFor2gFailure(policyDefinition, enforcingAdmin)) {
@@ -416,8 +443,6 @@ final class DevicePolicyEngine {
return;
}
- PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
-
boolean policyChanged = globalPolicyState.addPolicy(enforcingAdmin, value);
boolean policyAppliedOnAllUsers = applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
policyDefinition, enforcingAdmin, value, skipEnforcePolicy);
@@ -434,7 +459,7 @@ final class DevicePolicyEngine {
// TODO(b/285532044): remove hack and handle properly
if (!policyAppliedGlobally
&& policyDefinition.getPolicyKey().getIdentifier().equals(
- USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
+ USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value;
PolicyValue<Set<String>> parsedResolvedValue =
(PolicyValue<Set<String>>) globalPolicyState.getCurrentResolvedPolicy();
@@ -459,6 +484,7 @@ final class DevicePolicyEngine {
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
/**
* Removes any previously set policy for the provided {@code policyDefinition}
* (see {@link PolicyDefinition}) and {@code enforcingAdmin}.
@@ -472,6 +498,11 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
+
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ decreasePolicySizeForAdmin(policyState, enforcingAdmin);
+ }
+
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
if (policyChanged) {
@@ -687,7 +718,6 @@ final class DevicePolicyEngine {
* <p>Note that this will always return at most one item for policies that do not require
* additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
* {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
- *
*/
@NonNull
<V> Set<PolicyKey> getLocalPolicyKeysSetByAdmin(
@@ -723,7 +753,6 @@ final class DevicePolicyEngine {
* <p>Note that this will always return at most one item for policies that do not require
* additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
* {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
- *
*/
@NonNull
<V> Set<PolicyKey> getLocalPolicyKeysSetByAllAdmins(
@@ -964,7 +993,7 @@ final class DevicePolicyEngine {
EnforcingAdmin callingAdmin,
PolicyDefinition<V> policyDefinition,
int userId) {
- for (EnforcingAdmin admin: policyState.getPoliciesSetByAdmins().keySet()) {
+ for (EnforcingAdmin admin : policyState.getPoliciesSetByAdmins().keySet()) {
// We're sending a separate broadcast for the calling admin with the result.
if (admin.equals(callingAdmin)) {
continue;
@@ -1152,7 +1181,7 @@ final class DevicePolicyEngine {
try {
if (packageManager.getPackageInfo(packageName, 0, userId) == null
|| packageManager.getActivityInfo(
- policies.get(admin).getValue(), 0, userId) == null) {
+ policies.get(admin).getValue(), 0, userId) == null) {
Slogf.e(TAG, String.format(
"Persistent preferred activity in package %s not found for "
+ "user %d, removing policy for admin",
@@ -1450,6 +1479,97 @@ final class DevicePolicyEngine {
return false;
}
+ /**
+ * Calculate the size of a policy in bytes
+ */
+
+ private static <V> int sizeOf(PolicyValue<V> value) {
+ try {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(value, /* flags= */ 0);
+
+ parcel.setDataPosition(0);
+
+ byte[] bytes;
+
+ bytes = parcel.marshall();
+ return bytes.length;
+ } catch (Exception e) {
+ Log.e(TAG, "Error calculating size of policy: " + e);
+ return 0;
+ }
+ }
+
+ /**
+ * Checks if the policy already exists and removes the current size to prevent recording the
+ * same policy twice.
+ *
+ * Checks if the new sum of the size of all policies is less than the maximum sum of policies
+ * size per admin and returns true.
+ *
+ * If the policy size limit is reached then send policy result to admin and return false.
+ */
+
+ private <V> boolean handleAdminPolicySizeLimit(PolicyState<V> policyState, EnforcingAdmin admin,
+ PolicyValue<V> value, PolicyDefinition policyDefinition, int userId) {
+ int currentSize = 0;
+ if (mAdminPolicySize.contains(admin.getUserId())
+ && mAdminPolicySize.get(
+ admin.getUserId()).containsKey(admin)) {
+ currentSize = mAdminPolicySize.get(admin.getUserId()).get(admin);
+ }
+ if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+ currentSize -= sizeOf(policyState.getPoliciesSetByAdmins().get(admin));
+ }
+ int policySize = sizeOf(value);
+ if (currentSize + policySize < POLICY_SIZE_LIMIT) {
+ increasePolicySizeForAdmin(admin, policySize);
+ return true;
+ } else {
+ sendPolicyResultToAdmin(
+ admin,
+ policyDefinition,
+ RESULT_FAILURE_STORAGE_LIMIT_REACHED,
+ userId);
+ return false;
+ }
+ }
+
+ /**
+ * Increase the int in mAdminPolicySize representing the size of the sum of all
+ * active policies for that admin.
+ */
+
+ private <V> void increasePolicySizeForAdmin(EnforcingAdmin admin, int policySize) {
+ if (!mAdminPolicySize.contains(admin.getUserId())) {
+ mAdminPolicySize.put(admin.getUserId(), new HashMap<>());
+ }
+ if (!mAdminPolicySize.get(admin.getUserId()).containsKey(admin)) {
+ mAdminPolicySize.get(admin.getUserId()).put(admin, /* size= */ 0);
+ }
+ mAdminPolicySize.get(admin.getUserId()).put(admin,
+ mAdminPolicySize.get(admin.getUserId()).get(admin) + policySize);
+ }
+
+ /**
+ * Decrease the int in mAdminPolicySize representing the size of the sum of all
+ * active policies for that admin.
+ */
+
+ private <V> void decreasePolicySizeForAdmin(PolicyState<V> policyState, EnforcingAdmin admin) {
+ if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+ mAdminPolicySize.get(admin.getUserId()).put(admin,
+ mAdminPolicySize.get(admin.getUserId()).get(admin) - sizeOf(
+ policyState.getPoliciesSetByAdmins().get(admin)));
+ }
+ if (mAdminPolicySize.get(admin.getUserId()).get(admin) <= 0) {
+ mAdminPolicySize.get(admin.getUserId()).remove(admin);
+ }
+ if (mAdminPolicySize.get(admin.getUserId()).isEmpty()) {
+ mAdminPolicySize.remove(admin.getUserId());
+ }
+ }
+
@NonNull
private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) {
synchronized (mLock) {
@@ -1508,11 +1628,13 @@ final class DevicePolicyEngine {
clear();
write();
}
+
private void clear() {
synchronized (mLock) {
mGlobalPolicies.clear();
mLocalPolicies.clear();
mEnforcingAdmins.clear();
+ mAdminPolicySize.clear();
}
}
@@ -1553,7 +1675,11 @@ final class DevicePolicyEngine {
private static final String TAG_POLICY_STATE_ENTRY = "policy-state-entry";
private static final String TAG_POLICY_KEY_ENTRY = "policy-key-entry";
private static final String TAG_ENFORCING_ADMINS_ENTRY = "enforcing-admins-entry";
+ private static final String TAG_ENFORCING_ADMIN_AND_SIZE = "enforcing-admin-and-size";
+ private static final String TAG_ENFORCING_ADMIN = "enforcing-admin";
+ private static final String TAG_POLICY_SUM_SIZE = "policy-sum-size";
private static final String ATTR_USER_ID = "user-id";
+ private static final String ATTR_POLICY_SUM_SIZE = "size";
private final File mFile;
@@ -1595,6 +1721,7 @@ final class DevicePolicyEngine {
writeLocalPoliciesInner(serializer);
writeGlobalPoliciesInner(serializer);
writeEnforcingAdminsInner(serializer);
+ writeEnforcingAdminSizeInner(serializer);
}
private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
@@ -1652,6 +1779,30 @@ final class DevicePolicyEngine {
}
}
+ private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
+ throws IOException {
+ if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+ if (mAdminPolicySize != null) {
+ for (int i = 0; i < mAdminPolicySize.size(); i++) {
+ int userId = mAdminPolicySize.keyAt(i);
+ for (EnforcingAdmin admin : mAdminPolicySize.get(
+ userId).keySet()) {
+ serializer.startTag(/* namespace= */ null,
+ TAG_ENFORCING_ADMIN_AND_SIZE);
+ serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ admin.saveToXml(serializer);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+ serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
+ mAdminPolicySize.get(userId).get(admin));
+ serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+ serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
+ }
+ }
+ }
+ }
+ }
+
void readFromFileLocked() {
if (!mFile.exists()) {
Log.d(TAG, "" + mFile + " doesn't exist");
@@ -1689,6 +1840,9 @@ final class DevicePolicyEngine {
case TAG_ENFORCING_ADMINS_ENTRY:
readEnforcingAdminsInner(parser);
break;
+ case TAG_ENFORCING_ADMIN_AND_SIZE:
+ readEnforcingAdminAndSizeInner(parser);
+ break;
default:
Slogf.wtf(TAG, "Unknown tag " + tag);
}
@@ -1767,5 +1921,37 @@ final class DevicePolicyEngine {
}
mEnforcingAdmins.get(admin.getUserId()).add(admin);
}
+
+ private void readEnforcingAdminAndSizeInner(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ EnforcingAdmin admin = null;
+ int size = 0;
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ String tag = parser.getName();
+ switch (tag) {
+ case TAG_ENFORCING_ADMIN:
+ admin = EnforcingAdmin.readFromXml(parser);
+ break;
+ case TAG_POLICY_SUM_SIZE:
+ size = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
+ break;
+ default:
+ Slogf.wtf(TAG, "Unknown tag " + tag);
+ }
+ }
+ if (admin == null) {
+ Slogf.wtf(TAG, "Error parsing enforcingAdmins, EnforcingAdmin is null.");
+ return;
+ }
+ if (size <= 0) {
+ Slogf.wtf(TAG, "Error parsing policy size, size is " + size);
+ return;
+ }
+ if (!mAdminPolicySize.contains(admin.getUserId())) {
+ mAdminPolicySize.put(admin.getUserId(), new HashMap<>());
+ }
+ mAdminPolicySize.get(admin.getUserId()).put(admin, size);
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f60493289bfb..6aa135a3eaa9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -241,7 +241,6 @@ import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
import static android.provider.Telephony.Carriers.INVALID_APN_ID;
import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
-
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -23455,7 +23454,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public DevicePolicyState getDevicePolicyState() {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
return mInjector.binderWithCleanCallingIdentity(mDevicePolicyEngine::getDevicePolicyState);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
index 9fe3749755da..7e17ef111cf0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy.flags;
+import static com.android.server.devicepolicy.flags.Flags.devicePolicySizeTrackingEnabled;
import static com.android.server.devicepolicy.flags.Flags.policyEngineMigrationV2Enabled;
import android.os.Binder;
@@ -28,4 +29,10 @@ public final class FlagUtils {
return policyEngineMigrationV2Enabled();
});
}
+
+ public static boolean isDevicePolicySizeTrackingEnabled() {
+ return Binder.withCleanCallingIdentity(() -> {
+ return devicePolicySizeTrackingEnabled();
+ });
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
index 00702a9c26fa..0dde496e7285 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
@@ -5,4 +5,10 @@ flag {
namespace: "enterprise"
description: "V2 of the policy engine migrations for Android V"
bug: "289520697"
+}
+flag {
+ name: "device_policy_size_tracking_enabled"
+ namespace: "enterprise"
+ description: "Add feature to track the total policy size and have a max threshold."
+ bug: "281543351"
} \ No newline at end of file
diff --git a/services/foldables/devicestateprovider/Android.bp b/services/foldables/devicestateprovider/Android.bp
new file mode 100644
index 000000000000..34737eff8e6d
--- /dev/null
+++ b/services/foldables/devicestateprovider/Android.bp
@@ -0,0 +1,13 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "foldable-device-state-provider",
+ srcs: [
+ "src/**/*.java"
+ ],
+ libs: [
+ "services",
+ ],
+}
diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS
index b2dcd0c0f3c4..573284419a48 100644
--- a/services/foldables/devicestateprovider/OWNERS
+++ b/services/foldables/devicestateprovider/OWNERS
@@ -1,6 +1,6 @@
akulian@google.com
-kennethford@google.com
jiamingliu@google.com
kchyn@google.com
+kennethford@google.com
nickchameyev@google.com
nicomazz@google.com \ No newline at end of file
diff --git a/services/foldables/devicestateprovider/README.md b/services/foldables/devicestateprovider/README.md
new file mode 100644
index 000000000000..90174c0cdfc7
--- /dev/null
+++ b/services/foldables/devicestateprovider/README.md
@@ -0,0 +1,3 @@
+# Foldable Device State Provider library
+
+This library provides foldable-specific classes that could be used to implement a custom DeviceStateProvider. \ No newline at end of file
diff --git a/services/foldables/devicestateprovider/TEST_MAPPING b/services/foldables/devicestateprovider/TEST_MAPPING
new file mode 100644
index 000000000000..47de131803c5
--- /dev/null
+++ b/services/foldables/devicestateprovider/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "foldable-device-state-provider-tests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
new file mode 100644
index 000000000000..aea46d1ce329
--- /dev/null
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.policy;
+
+import static android.hardware.SensorManager.SENSOR_DELAY_FASTEST;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManager;
+import android.hardware.display.DisplayManager;
+import android.os.Trace;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.devicestate.DeviceState;
+import com.android.server.devicestate.DeviceStateProvider;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+
+/**
+ * Device state provider for foldable devices.
+ *
+ * It is an implementation of {@link DeviceStateProvider} tailored specifically for
+ * foldable devices and allows simple callback-based configuration with hall sensor
+ * and hinge angle sensor values.
+ */
+public final class FoldableDeviceStateProvider implements DeviceStateProvider,
+ SensorEventListener, PowerManager.OnThermalStatusChangedListener,
+ DisplayManager.DisplayListener {
+
+ private static final String TAG = "FoldableDeviceStateProvider";
+ private static final boolean DEBUG = false;
+
+ // Lock for internal state.
+ private final Object mLock = new Object();
+
+ // List of supported states in ascending order based on their identifier.
+ private final DeviceState[] mOrderedStates;
+
+ // Map of state identifier to a boolean supplier that returns true when all required conditions
+ // are met for the device to be in the state.
+ private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>();
+
+ private final Sensor mHingeAngleSensor;
+ private final DisplayManager mDisplayManager;
+ private final Sensor mHallSensor;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private Listener mListener = null;
+ @GuardedBy("mLock")
+ private int mLastReportedState = INVALID_DEVICE_STATE;
+ @GuardedBy("mLock")
+ private SensorEvent mLastHingeAngleSensorEvent = null;
+ @GuardedBy("mLock")
+ private SensorEvent mLastHallSensorEvent = null;
+ @GuardedBy("mLock")
+ private @PowerManager.ThermalStatus
+ int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
+ @GuardedBy("mLock")
+ private boolean mIsScreenOn = false;
+
+ @GuardedBy("mLock")
+ private boolean mPowerSaveModeEnabled;
+
+ public FoldableDeviceStateProvider(@NonNull Context context,
+ @NonNull SensorManager sensorManager,
+ @NonNull Sensor hingeAngleSensor,
+ @NonNull Sensor hallSensor,
+ @NonNull DisplayManager displayManager,
+ @NonNull DeviceStateConfiguration[] deviceStateConfigurations) {
+
+ Preconditions.checkArgument(deviceStateConfigurations.length > 0,
+ "Device state configurations array must not be empty");
+
+ mHingeAngleSensor = hingeAngleSensor;
+ mHallSensor = hallSensor;
+ mDisplayManager = displayManager;
+
+ sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST);
+ sensorManager.registerListener(this, mHallSensor, SENSOR_DELAY_FASTEST);
+
+ mOrderedStates = new DeviceState[deviceStateConfigurations.length];
+ for (int i = 0; i < deviceStateConfigurations.length; i++) {
+ final DeviceStateConfiguration configuration = deviceStateConfigurations[i];
+ mOrderedStates[i] = configuration.mDeviceState;
+
+ if (mStateConditions.get(configuration.mDeviceState.getIdentifier()) != null) {
+ throw new IllegalArgumentException("Device state configurations must have unique"
+ + " device state identifiers, found duplicated identifier: " +
+ configuration.mDeviceState.getIdentifier());
+ }
+
+ mStateConditions.put(configuration.mDeviceState.getIdentifier(), () ->
+ configuration.mPredicate.apply(this));
+ }
+
+ mDisplayManager.registerDisplayListener(
+ /* listener = */ this,
+ /* handler= */ null,
+ /* eventsMask= */ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+
+ Arrays.sort(mOrderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
+
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager != null) {
+ // If any of the device states are thermal sensitive, i.e. it should be disabled when
+ // the device is overheating, then we will update the list of supported states when
+ // thermal status changes.
+ if (hasThermalSensitiveState(deviceStateConfigurations)) {
+ powerManager.addThermalStatusListener(this);
+ }
+
+ // If any of the device states are power sensitive, i.e. it should be disabled when
+ // power save mode is enabled, then we will update the list of supported states when
+ // power save mode is toggled.
+ if (hasPowerSaveSensitiveState(deviceStateConfigurations)) {
+ IntentFilter filter = new IntentFilter(
+ PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL.equals(
+ intent.getAction())) {
+ onPowerSaveModeChanged(powerManager.isPowerSaveMode());
+ }
+ }
+ };
+ context.registerReceiver(receiver, filter);
+ }
+ }
+ }
+
+ @Override
+ public void setListener(Listener listener) {
+ synchronized (mLock) {
+ if (mListener != null) {
+ throw new RuntimeException("Provider already has a listener set.");
+ }
+ mListener = listener;
+ }
+ notifySupportedStatesChanged(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
+ notifyDeviceStateChangedIfNeeded();
+ }
+
+ /** Notifies the listener that the set of supported device states has changed. */
+ private void notifySupportedStatesChanged(@SupportedStatesUpdatedReason int reason) {
+ List<DeviceState> supportedStates = new ArrayList<>();
+ Listener listener;
+ synchronized (mLock) {
+ if (mListener == null) {
+ return;
+ }
+ listener = mListener;
+ for (DeviceState deviceState : mOrderedStates) {
+ if (isThermalStatusCriticalOrAbove(mThermalStatus)
+ && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ continue;
+ }
+ if (mPowerSaveModeEnabled && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
+ continue;
+ }
+ supportedStates.add(deviceState);
+ }
+ }
+
+ listener.onSupportedDeviceStatesChanged(
+ supportedStates.toArray(new DeviceState[supportedStates.size()]), reason);
+ }
+
+ /** Computes the current device state and notifies the listener of a change, if needed. */
+ void notifyDeviceStateChangedIfNeeded() {
+ int stateToReport = INVALID_DEVICE_STATE;
+ Listener listener;
+ synchronized (mLock) {
+ if (mListener == null) {
+ return;
+ }
+
+ listener = mListener;
+
+ int newState = INVALID_DEVICE_STATE;
+ for (int i = 0; i < mOrderedStates.length; i++) {
+ int state = mOrderedStates[i].getIdentifier();
+ if (DEBUG) {
+ Slog.d(TAG, "Checking conditions for " + mOrderedStates[i].getName() + "("
+ + i + ")");
+ }
+ boolean conditionSatisfied;
+ try {
+ conditionSatisfied = mStateConditions.get(state).getAsBoolean();
+ } catch (IllegalStateException e) {
+ // Failed to compute the current state based on current available data. Continue
+ // with the expectation that notifyDeviceStateChangedIfNeeded() will be called
+ // when a callback with the missing data is triggered. May trigger another state
+ // change if another state is satisfied currently.
+ Slog.w(TAG, "Unable to check current state = " + state, e);
+ dumpSensorValues();
+ continue;
+ }
+
+ if (conditionSatisfied) {
+ if (DEBUG) {
+ Slog.d(TAG, "Device State conditions satisfied, transition to " + state);
+ }
+ newState = state;
+ break;
+ }
+ }
+ if (newState == INVALID_DEVICE_STATE) {
+ Slog.e(TAG, "No declared device states match any of the required conditions.");
+ dumpSensorValues();
+ }
+
+ if (newState != INVALID_DEVICE_STATE && newState != mLastReportedState) {
+ mLastReportedState = newState;
+ stateToReport = newState;
+ }
+ }
+
+ if (stateToReport != INVALID_DEVICE_STATE) {
+ listener.onStateChanged(stateToReport);
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (mLock) {
+ if (event.sensor == mHallSensor) {
+ mLastHallSensorEvent = event;
+ } else if (event.sensor == mHingeAngleSensor) {
+ mLastHingeAngleSensorEvent = event;
+ }
+ }
+ notifyDeviceStateChangedIfNeeded();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Do nothing.
+ }
+
+ private float getSensorValue(@Nullable SensorEvent sensorEvent) {
+ if (sensorEvent == null) {
+ throw new IllegalStateException("Have not received sensor event.");
+ }
+
+ if (sensorEvent.values.length < 1) {
+ throw new IllegalStateException("Values in the sensor event are empty");
+ }
+
+ return sensorEvent.values[0];
+ }
+
+ @GuardedBy("mLock")
+ private void dumpSensorValues() {
+ Slog.i(TAG, "Sensor values:");
+ dumpSensorValues("Hall Sensor", mHallSensor, mLastHallSensorEvent);
+ dumpSensorValues("Hinge Angle Sensor",mHingeAngleSensor, mLastHingeAngleSensorEvent);
+ Slog.i(TAG, "isScreenOn: " + isScreenOn());
+ }
+
+ @GuardedBy("mLock")
+ private void dumpSensorValues(String sensorType, Sensor sensor, @Nullable SensorEvent event) {
+ String sensorString = sensor == null ? "null" : sensor.getName();
+ String eventValues = event == null ? "null" : Arrays.toString(event.values);
+ Slog.i(TAG, sensorType + " : " + sensorString + " : " + eventValues);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // Could potentially be moved to the background if needed.
+ try {
+ Trace.beginSection("FoldableDeviceStateProvider#onDisplayChanged()");
+ int displayState = mDisplayManager.getDisplay(displayId).getState();
+ synchronized (mLock) {
+ mIsScreenOn = displayState == Display.STATE_ON;
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+ }
+
+ /**
+ * Configuration for a single device state, contains information about the state like
+ * identifier, name, flags and a predicate that should return true if the state should
+ * be selected.
+ */
+ public static class DeviceStateConfiguration {
+ private final DeviceState mDeviceState;
+ private final Function<FoldableDeviceStateProvider, Boolean> mPredicate;
+
+ private DeviceStateConfiguration(DeviceState deviceState,
+ Function<FoldableDeviceStateProvider, Boolean> predicate) {
+ mDeviceState = deviceState;
+ mPredicate = predicate;
+ }
+
+ public static DeviceStateConfiguration createConfig(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ @DeviceState.DeviceStateFlags int flags,
+ Function<FoldableDeviceStateProvider, Boolean> predicate
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, flags),
+ predicate);
+ }
+
+ public static DeviceStateConfiguration createConfig(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ Function<FoldableDeviceStateProvider, Boolean> predicate
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, /* flags= */ 0),
+ predicate);
+ }
+
+ /**
+ * Creates a device state configuration for a closed tent-mode aware state.
+ *
+ * During tent mode:
+ * - The inner display is OFF
+ * - The outer display is ON
+ * - The device is partially unfolded (left and right edges could be on the table)
+ * In this mode the device the device so it could be used in a posture where both left
+ * and right edges of the unfolded device are on the table.
+ *
+ * The predicate returns false after the hinge angle reaches
+ * {@code tentModeSwitchAngleDegrees}. Then it switches back only when the hinge angle
+ * becomes less than {@code maxClosedAngleDegrees}. Hinge angle is 0 degrees when the device
+ * is fully closed and 180 degrees when it is fully unfolded.
+ *
+ * For example, when tentModeSwitchAngleDegrees = 90 and maxClosedAngleDegrees = 5 degrees:
+ * - when unfolding the device from fully closed posture (last state == closed or it is
+ * undefined yet) this state will become not matching after reaching the angle
+ * of 90 degrees, it allows the device to switch the outer display to the inner display
+ * only when reaching this threshold
+ * - when folding (last state != 'closed') this state will become matching after reaching
+ * the angle less than 5 degrees and when hall sensor detected that the device is closed,
+ * so the switch from the inner display to the outer will become only when the device
+ * is fully closed.
+ *
+ * @param identifier state identifier
+ * @param name state name
+ * @param flags state flags
+ * @param minClosedAngleDegrees minimum (inclusive) hinge angle value for the closed state
+ * @param maxClosedAngleDegrees maximum (non-inclusive) hinge angle value for the closed
+ * state
+ * @param tentModeSwitchAngleDegrees the angle when this state should switch when unfolding
+ * @return device state configuration
+ */
+ public static DeviceStateConfiguration createTentModeClosedState(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ @DeviceState.DeviceStateFlags int flags,
+ int minClosedAngleDegrees,
+ int maxClosedAngleDegrees,
+ int tentModeSwitchAngleDegrees
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, flags),
+ (stateContext) -> {
+ final boolean hallSensorClosed = stateContext.isHallSensorClosed();
+ final float hingeAngle = stateContext.getHingeAngle();
+ final int lastState = stateContext.getLastReportedDeviceState();
+ final boolean isScreenOn = stateContext.isScreenOn();
+
+ final int switchingDegrees =
+ isScreenOn ? tentModeSwitchAngleDegrees : maxClosedAngleDegrees;
+
+ final int closedDeviceState = identifier;
+ final boolean isLastStateClosed = lastState == closedDeviceState
+ || lastState == INVALID_DEVICE_STATE;
+
+ final boolean shouldBeClosedBecauseTentMode = isLastStateClosed
+ && hingeAngle >= minClosedAngleDegrees
+ && hingeAngle < switchingDegrees;
+
+ final boolean shouldBeClosedBecauseFullyShut = hallSensorClosed
+ && hingeAngle >= minClosedAngleDegrees
+ && hingeAngle < maxClosedAngleDegrees;
+
+ return shouldBeClosedBecauseFullyShut || shouldBeClosedBecauseTentMode;
+ });
+ }
+ }
+
+ /**
+ * @return Whether the screen is on.
+ */
+ public boolean isScreenOn() {
+ synchronized (mLock) {
+ return mIsScreenOn;
+ }
+ }
+ /**
+ * @return current hinge angle value of a foldable device
+ */
+ public float getHingeAngle() {
+ synchronized (mLock) {
+ return getSensorValue(mLastHingeAngleSensorEvent);
+ }
+ }
+
+ /**
+ * @return true if hall sensor detected that the device is closed (fully shut)
+ */
+ public boolean isHallSensorClosed() {
+ synchronized (mLock) {
+ return getSensorValue(mLastHallSensorEvent) > 0f;
+ }
+ }
+
+ /**
+ * @return last reported device state
+ */
+ public int getLastReportedDeviceState() {
+ synchronized (mLock) {
+ return mLastReportedState;
+ }
+ }
+
+ @VisibleForTesting
+ void onPowerSaveModeChanged(boolean isPowerSaveModeEnabled) {
+ synchronized (mLock) {
+ if (mPowerSaveModeEnabled != isPowerSaveModeEnabled) {
+ mPowerSaveModeEnabled = isPowerSaveModeEnabled;
+ notifySupportedStatesChanged(
+ isPowerSaveModeEnabled ? SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED
+ : SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED);
+ }
+ }
+ }
+
+ @Override
+ public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) {
+ int previousThermalStatus;
+ synchronized (mLock) {
+ previousThermalStatus = mThermalStatus;
+ mThermalStatus = thermalStatus;
+ }
+
+ boolean isThermalStatusCriticalOrAbove = isThermalStatusCriticalOrAbove(thermalStatus);
+ boolean isPreviousThermalStatusCriticalOrAbove =
+ isThermalStatusCriticalOrAbove(previousThermalStatus);
+ if (isThermalStatusCriticalOrAbove != isPreviousThermalStatusCriticalOrAbove) {
+ Slog.i(TAG, "Updating supported device states due to thermal status change."
+ + " isThermalStatusCriticalOrAbove: " + isThermalStatusCriticalOrAbove);
+ notifySupportedStatesChanged(
+ isThermalStatusCriticalOrAbove
+ ? SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
+ : SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL);
+ }
+ }
+
+ private static boolean isThermalStatusCriticalOrAbove(
+ @PowerManager.ThermalStatus int thermalStatus) {
+ switch (thermalStatus) {
+ case PowerManager.THERMAL_STATUS_CRITICAL:
+ case PowerManager.THERMAL_STATUS_EMERGENCY:
+ case PowerManager.THERMAL_STATUS_SHUTDOWN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean hasThermalSensitiveState(DeviceStateConfiguration[] deviceStates) {
+ for (int i = 0; i < deviceStates.length; i++) {
+ DeviceStateConfiguration state = deviceStates[i];
+ if (state.mDeviceState
+ .hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasPowerSaveSensitiveState(DeviceStateConfiguration[] deviceStates) {
+ for (int i = 0; i < deviceStates.length; i++) {
+ if (deviceStates[i].mDeviceState
+ .hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp
new file mode 100644
index 000000000000..a8db05e99179
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/Android.bp
@@ -0,0 +1,30 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "foldable-device-state-provider-tests",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libstaticjvmtiagent",
+ ],
+ static_libs: [
+ "services",
+ "foldable-device-state-provider",
+ "androidx.test.rules",
+ "junit",
+ "truth-prebuilt",
+ "mockito-target-extended-minus-junit4",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.ext.junit",
+ "testables",
+ ],
+ test_suites: ["device-tests"]
+}
diff --git a/packages/CredentialManager/wear/res/values/themes.xml b/services/foldables/devicestateprovider/tests/AndroidManifest.xml
index 22329e9ff2ce..736613d9ad90 100644
--- a/packages/CredentialManager/wear/res/values/themes.xml
+++ b/services/foldables/devicestateprovider/tests/AndroidManifest.xml
@@ -14,11 +14,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<resources>
- <style name="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowIsTranslucent">true</item>
- </style>
-</resources> \ No newline at end of file
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.foldablesdevicestatelib.tests">
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.foldablesdevicestatelib.tests"
+ android:label="Tests for foldable-device-state-provider library">
+ </instrumentation>
+
+</manifest> \ No newline at end of file
diff --git a/services/foldables/devicestateprovider/tests/AndroidTest.xml b/services/foldables/devicestateprovider/tests/AndroidTest.xml
new file mode 100644
index 000000000000..f5fdac75f7cd
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="foldable-device-state-provider tests">
+ <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="foldable-device-state-provider-tests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.foldablesdevicestatelib.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration> \ No newline at end of file
diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
new file mode 100644
index 000000000000..8fa4ce592777
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.policy;
+
+
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
+import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
+
+import static com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration.createConfig;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.PowerManager;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import com.android.server.devicestate.DeviceState;
+import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.devicestate.DeviceStateProvider.Listener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+/**
+ * Unit tests for {@link FoldableDeviceStateProvider}.
+ * <p/>
+ * Run with <code>atest FoldableDeviceStateProviderTest</code>.
+ */
+@RunWith(AndroidTestingRunner.class)
+public final class FoldableDeviceStateProviderTest {
+
+ private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
+ DeviceState[].class);
+ @Captor
+ private ArgumentCaptor<Integer> mIntegerCaptor;
+ @Captor
+ private ArgumentCaptor<DisplayManager.DisplayListener> mDisplayListenerCaptor;
+ @Mock
+ private SensorManager mSensorManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private InputSensorInfo mInputSensorInfo;
+ private Sensor mHallSensor;
+ private Sensor mHingeAngleSensor;
+ @Mock
+ private DisplayManager mDisplayManager;
+ private FoldableDeviceStateProvider mProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mHallSensor = new Sensor(mInputSensorInfo);
+ mHingeAngleSensor = new Sensor(mInputSensorInfo);
+ }
+
+ @Test
+ public void create_emptyConfiguration_throwsException() {
+ assertThrows(IllegalArgumentException.class, this::createProvider);
+ }
+
+ @Test
+ public void create_duplicatedDeviceStateIdentifiers_throwsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> createProvider(
+ createConfig(
+ /* identifier= */ 0, /* name= */ "ONE", (c) -> true),
+ createConfig(
+ /* identifier= */ 0, /* name= */ "TWO", (c) -> true)
+ ));
+ }
+
+ @Test
+ public void create_allMatchingStatesDefaultsToTheFirstIdentifier() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true),
+ createConfig(
+ /* identifier= */ 2, /* name= */ "TWO", (c) -> true),
+ createConfig(
+ /* identifier= */ 3, /* name= */ "THREE", (c) -> true)
+ );
+
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ final DeviceState[] expectedStates = new DeviceState[]{
+ new DeviceState(1, "ONE", /* flags= */ 0),
+ new DeviceState(2, "TWO", /* flags= */ 0),
+ new DeviceState(3, "THREE", /* flags= */ 0),
+ };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void create_multipleMatchingStatesDefaultsToTheLowestIdentifier() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> false),
+ createConfig(
+ /* identifier= */ 3, /* name= */ "THREE", (c) -> false),
+ createConfig(
+ /* identifier= */ 4, /* name= */ "FOUR", (c) -> true),
+ createConfig(
+ /* identifier= */ 2, /* name= */ "TWO", (c) -> true)
+ );
+
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hingeAngleUpdatedFirstTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getHingeAngle() >= 90f));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener, never()).onStateChanged(anyInt());
+ clearInvocations(listener);
+
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 100f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hallSensorUpdatedFirstTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> !c.isHallSensorClosed()),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ FoldableDeviceStateProvider::isHallSensorClosed));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener, never()).onStateChanged(anyInt());
+ clearInvocations(listener);
+
+ // Hall sensor value '1f' is for the closed state
+ sendSensorEvent(mHallSensor, /* value= */ 1f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hingeAngleUpdatedSecondTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getHingeAngle() >= 90f));
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 30f);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 100f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hallSensorUpdatedSecondTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> !c.isHallSensorClosed()),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ FoldableDeviceStateProvider::isHallSensorClosed));
+ sendSensorEvent(mHallSensor, /* value= */ 0f);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ // Hall sensor value '1f' is for the closed state
+ sendSensorEvent(mHallSensor, /* value= */ 1f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_invalidSensorValues_onStateChangedIsNotTriggered() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getHingeAngle() >= 90f));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ clearInvocations(listener);
+
+ // First, switch to a non-default state.
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 100f);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+
+ clearInvocations(listener);
+
+ // Then, send an invalid sensor event, verify that onStateChanged() is not triggered.
+ sendInvalidSensorEvent(mHingeAngleSensor);
+
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
+ }
+
+ @Test
+ public void test_nullSensorValues_noExceptionThrown() throws Exception {
+ createProvider(createConfig( /* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f));
+ sendInvalidSensorEvent(null);
+ }
+
+ @Test
+ public void test_flagDisableWhenThermalStatusCritical() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED",
+ (c) -> c.getHingeAngle() < 5f),
+ createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 3, /* name= */ "OPENED",
+ (c) -> c.getHingeAngle() < 180f),
+ createConfig(/* identifier= */ 4, /* name= */ "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ (c) -> true));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)},
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_MODERATE);
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be disabled.
+ mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_CRITICAL);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */)},
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be re-enabled.
+ mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)},
+ mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void test_flagDisableWhenPowerSaveEnabled() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED",
+ (c) -> c.getHingeAngle() < 5f),
+ createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 3, /* name= */ "OPENED",
+ (c) -> c.getHingeAngle() < 180f),
+ createConfig(/* identifier= */ 4, /* name= */ "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ (c) -> true));
+ mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be disabled due to power save being enabled.
+ mProvider.onPowerSaveModeChanged(true /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */) },
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be re-enabled.
+ mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void test_previousStateBasedPredicate() {
+ // Create a configuration where state TWO could be matched only if
+ // the previous state was 'THREE'
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> c.getHingeAngle() < 30f),
+ createConfig(
+ /* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getLastReportedDeviceState() == 3 && c.getHingeAngle() > 120f),
+ createConfig(
+ /* identifier= */ 3, /* name= */ "THREE",
+ (c) -> c.getHingeAngle() > 90f)
+ );
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 0f);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ // Check that the initial state is 'ONE'
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ // Should not match state 'TWO', it should match only state 'THREE'
+ // (because the previous state is not 'THREE', it is 'ONE')
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 180f);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(3, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ // Now it should match state 'TWO'
+ // (because the previous state is 'THREE' now)
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 180f);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void isScreenOn_afterDisplayChangedToOn_returnsTrue() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true)
+ );
+
+ setScreenOn(true);
+
+ assertThat(mProvider.isScreenOn()).isTrue();
+ }
+
+ @Test
+ public void isScreenOn_afterDisplayChangedToOff_returnsFalse() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true)
+ );
+
+ setScreenOn(false);
+
+ assertThat(mProvider.isScreenOn()).isFalse();
+ }
+
+ @Test
+ public void isScreenOn_afterDisplayChangedToOnThenOff_returnsFalse() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true)
+ );
+
+ setScreenOn(true);
+ setScreenOn(false);
+
+ assertThat(mProvider.isScreenOn()).isFalse();
+ }
+
+ private void setScreenOn(boolean isOn) {
+ Display mockDisplay = mock(Display.class);
+ int state = isOn ? STATE_ON : STATE_OFF;
+ when(mockDisplay.getState()).thenReturn(state);
+ when(mDisplayManager.getDisplay(eq(DEFAULT_DISPLAY))).thenReturn(mockDisplay);
+ mDisplayListenerCaptor.getValue().onDisplayChanged(DEFAULT_DISPLAY);
+ }
+
+ private void sendSensorEvent(Sensor sensor, float value) {
+ SensorEvent event = mock(SensorEvent.class);
+ event.sensor = sensor;
+ try {
+ FieldSetter.setField(event, event.getClass().getField("values"),
+ new float[]{value});
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+
+ mProvider.onSensorChanged(event);
+ }
+
+ private void sendInvalidSensorEvent(Sensor sensor) {
+ SensorEvent event = mock(SensorEvent.class);
+ event.sensor = sensor;
+ try {
+ // Set empty values array to make the event invalid
+ FieldSetter.setField(event, event.getClass().getField("values"),
+ new float[]{});
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ mProvider.onSensorChanged(event);
+ }
+
+ private void createProvider(DeviceStateConfiguration... configurations) {
+ mProvider = new FoldableDeviceStateProvider(mContext, mSensorManager, mHingeAngleSensor,
+ mHallSensor, mDisplayManager, configurations);
+ verify(mDisplayManager)
+ .registerDisplayListener(
+ mDisplayListenerCaptor.capture(),
+ nullable(Handler.class),
+ anyLong());
+ }
+}
diff --git a/services/net/OWNERS b/services/net/OWNERS
index 62c5737a2e8e..c24680e9b06a 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,2 +1,2 @@
set noparent
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index 4a2bf75b4d2c..5d3eba86a725 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -17,24 +17,22 @@
package com.android.server.pm;
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
+
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.AppGlobals;
-import android.content.IIntentReceiver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
-import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -87,18 +85,6 @@ public class PackageManagerServiceTest {
@Test
public void testPackageRemoval() {
class PackageSenderImpl implements PackageSender {
- public void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final int flags, final String targetPkg,
- final IIntentReceiver finishedReceiver, final int[] userIds,
- int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
- @Nullable Bundle bOptions) {
- }
-
- public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
- boolean sendBootComplete, boolean includeStopped, int appId,
- int[] userIds, int[] instantUserIds, boolean isArchived, int dataLoaderType) {
- }
-
@Override
public void notifyPackageAdded(String packageName, int uid) {
}
@@ -113,9 +99,8 @@ public class PackageManagerServiceTest {
}
}
- PackageSenderImpl sender = new PackageSenderImpl();
PackageSetting setting = null;
- PackageRemovedInfo pri = new PackageRemovedInfo(sender);
+ PackageRemovedInfo pri = new PackageRemovedInfo();
// Initial conditions: nothing there
Assert.assertNull(pri.mRemovedUsers);
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
index 823ce4555ca8..05477197b991 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
@@ -112,4 +112,4 @@ class AppIdPermissionPolicyPermissionResetTest : BaseAppIdPermissionPolicyTest()
@JvmStatic
fun data(): Array<Action> = Action.values()
}
-} \ No newline at end of file
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
index f085bd7fd7f0..c44b2c50258e 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
@@ -43,8 +43,7 @@ class AppIdPermissionPolicyPermissionStatesTest : BaseAppIdPermissionPolicyTest(
@Parameterized.Parameter(0) lateinit var action: Action
@Before
- override fun setUp() {
- super.setUp()
+ fun setUp() {
if (action == Action.ON_USER_ADDED) {
createUserState(USER_ID_NEW)
}
@@ -881,4 +880,4 @@ class AppIdPermissionPolicyPermissionStatesTest : BaseAppIdPermissionPolicyTest(
@JvmStatic
fun data(): Array<Action> = Action.values()
}
-} \ No newline at end of file
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
new file mode 100644
index 000000000000..e4e336845fca
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.permission.test
+
+import android.content.pm.PermissionGroupInfo
+import android.content.pm.PermissionInfo
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.Permission
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.testutils.mock
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+class AppIdPermissionPolicyTest : BaseAppIdPermissionPolicyTest() {
+ @Test
+ fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
+ val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(parsedPermission)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ onAppIdRemoved(APP_ID_1)
+ }
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
+ " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
+ " flags $actualFlags should be null"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnStorageVolumeMounted_nonSystemAppAfterNonSystemUpdate_remainsRevoked() {
+ val permissionOwnerPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(installedPackageState)
+ addPermission(defaultPermission)
+ val oldFlags = PermissionFlags.INSTALL_REVOKED
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ onStorageVolumeMounted(null, listOf(installedPackageState.packageName), false)
+ }
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onStorageVolumeMounted() is called for a non-system app that requests a normal" +
+ " permission with existing INSTALL_REVOKED flag after a non-system-update" +
+ " (such as an OTA update), the actual permission flags should remain revoked." +
+ " The actual permission flags $actualFlags should match the expected flags" +
+ " $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageRemoved_packageIsRemoved_permissionDefinitionsAndStatesAreUpdated() {
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ requestedPermissions = setOf(PERMISSION_NAME_0),
+ permissions = listOf(defaultPermission)
+ )
+ )
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(defaultPermission)
+ val oldFlags = PermissionFlags.INSTALL_GRANTED
+ setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+
+ mutateState {
+ removePackageState(permissionOwnerPackageState)
+ with(appIdPermissionPolicy) {
+ onPackageRemoved(PACKAGE_NAME_0, APP_ID_0)
+ }
+ }
+
+ assertWithMessage(
+ "After onPackageRemoved() is called for a permission owner, the permission" +
+ " definitions owned by this package should be removed"
+ )
+ .that(getPermission(PERMISSION_NAME_0))
+ .isNull()
+
+ val app0ActualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ val app0ExpectedNewFlags = 0
+ assertWithMessage(
+ "After onPackageRemoved() is called for a permission owner, the permission states of" +
+ " this app should be trimmed. The actual permission flags $app0ActualFlags should" +
+ " match the expected flags $app0ExpectedNewFlags"
+ )
+ .that(app0ActualFlags)
+ .isEqualTo(app0ExpectedNewFlags)
+
+ val app1ActualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val app1ExpectedNewFlags = PermissionFlags.INSTALL_REVOKED
+ assertWithMessage(
+ "After onPackageRemoved() is called for a permission owner, the permission states of" +
+ " the permission requester should remain unchanged. The actual permission flags" +
+ " $app1ActualFlags should match the expected flags $app1ExpectedNewFlags"
+ )
+ .that(app1ActualFlags)
+ .isEqualTo(app1ExpectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.SOFT_RESTRICTED
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " soft restricted permission, UPGRADE_EXEMPT flag should be removed. The actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED,
+ isInstalledPackageSystem = true
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onPackageInstalled() is called for a system app that requests a runtime" +
+ " soft restricted permission, UPGRADE_EXEMPT flag should be retained. The actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {
+ val systemAppPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_2, requestedPermissions = setOf(PERMISSION_NAME_0)),
+ isSystem = true
+ )
+ addPackageState(systemAppPackageState)
+ }
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " soft restricted permission, and that permission is also requested by a system" +
+ " app in the same appId, UPGRADE_EXEMPT flag should be retained. The actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
+ val oldFlags = PermissionFlags.RESTRICTION_REVOKED
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " hard restricted permission that is not exempted. The actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
+ val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.INSTALLER_EXEMPT
+ testOnPackageInstalled(
+ oldFlags,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {}
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALLER_EXEMPT
+ assertWithMessage(
+ "After onPackageInstalled() is called for a non-system app that requests a runtime" +
+ " soft restricted permission that is exempted. The actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testOnPackageInstalled(
+ oldFlags: Int,
+ permissionInfoFlags: Int = 0,
+ isInstalledPackageSystem: Boolean = false,
+ additionalSetup: () -> Unit
+ ) {
+ val parsedPermission = mockParsedPermission(
+ PERMISSION_NAME_0,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+ flags = permissionInfoFlags
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPermission(parsedPermission)
+
+ additionalSetup()
+
+ mutateState {
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(PERMISSION_NAME_0),
+ ),
+ isSystem = isInstalledPackageSystem,
+ )
+ addPackageState(installedPackageState, newState)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags, newState)
+ with(appIdPermissionPolicy) {
+ onPackageInstalled(installedPackageState, USER_ID_0)
+ }
+ }
+ }
+
+ @Test
+ fun testOnStateMutated_notEmpty_isCalledForEachListener() {
+ val mockListener = mock<AppIdPermissionPolicy.OnPermissionFlagsChangedListener> {}
+ appIdPermissionPolicy.addOnPermissionFlagsChangedListener(mockListener)
+
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ onStateMutated()
+ }
+ }
+
+ verify(mockListener, times(1)).onStateMutated()
+ }
+
+ @Test
+ fun testGetPermissionTrees() {
+ val permissionTrees: IndexedMap<String, Permission>
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ permissionTrees = getPermissionTrees()
+ }
+ }
+
+ assertThat(oldState.systemState.permissionTrees).isEqualTo(permissionTrees)
+ }
+
+ @Test
+ fun testFindPermissionTree() {
+ val permissionTree = createSimplePermission(isTree = true)
+ val actualPermissionTree: Permission?
+ oldState.mutateSystemState().mutatePermissionTrees()[PERMISSION_TREE_NAME] = permissionTree
+
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ actualPermissionTree = findPermissionTree(PERMISSION_BELONGS_TO_A_TREE)
+ }
+ }
+
+ assertThat(actualPermissionTree).isEqualTo(permissionTree)
+ }
+
+ @Test
+ fun testAddPermissionTree() {
+ val permissionTree = createSimplePermission(isTree = true)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ addPermissionTree(permissionTree)
+ }
+ }
+
+ assertThat(newState.systemState.permissionTrees[PERMISSION_TREE_NAME])
+ .isEqualTo(permissionTree)
+ }
+
+ @Test
+ fun testGetPermissionGroups() {
+ val permissionGroups: IndexedMap<String, PermissionGroupInfo>
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ permissionGroups = getPermissionGroups()
+ }
+ }
+
+ assertThat(oldState.systemState.permissionGroups).isEqualTo(permissionGroups)
+ }
+
+ @Test
+ fun testGetPermissions() {
+ val permissions: IndexedMap<String, Permission>
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ permissions = getPermissions()
+ }
+ }
+
+ assertThat(oldState.systemState.permissions).isEqualTo(permissions)
+ }
+
+ @Test
+ fun testAddPermission() {
+ val permission = createSimplePermission()
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ addPermission(permission)
+ }
+ }
+
+ assertThat(newState.systemState.permissions[PERMISSION_NAME_0]).isEqualTo(permission)
+ }
+
+ @Test
+ fun testRemovePermission() {
+ val permission = createSimplePermission()
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ addPermission(permission)
+ removePermission(permission)
+ }
+ }
+
+ assertThat(newState.systemState.permissions[PERMISSION_NAME_0]).isNull()
+ }
+
+ @Test
+ fun testGetUidPermissionFlags() {
+ val uidPermissionFlags: IndexedMap<String, Int>?
+ GetStateScope(oldState).apply {
+ with(appIdPermissionPolicy) {
+ uidPermissionFlags = getUidPermissionFlags(APP_ID_0, USER_ID_0)
+ }
+ }
+
+ assertThat(oldState.userStates[USER_ID_0]!!.appIdPermissionFlags[APP_ID_0])
+ .isEqualTo(uidPermissionFlags)
+ }
+
+ @Test
+ fun testUpdateAndGetPermissionFlags() {
+ val flags = PermissionFlags.INSTALL_GRANTED
+ var actualFlags = 0
+ mutateState {
+ with(appIdPermissionPolicy) {
+ updatePermissionFlags(
+ APP_ID_0,
+ USER_ID_0,
+ PERMISSION_NAME_0,
+ PermissionFlags.MASK_ALL,
+ flags
+ )
+ actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ }
+ }
+
+ assertThat(actualFlags).isEqualTo(flags)
+ }
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
index 7966c5c4d961..ec84bc329674 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
@@ -34,7 +34,6 @@ import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.permission.AppIdPermissionPolicy
import com.android.server.permission.access.permission.Permission
-import com.android.server.permission.access.permission.PermissionFlags
import com.android.server.permission.access.util.hasBits
import com.android.server.pm.parsing.PackageInfoUtils
import com.android.server.pm.pkg.AndroidPackage
@@ -45,10 +44,8 @@ import com.android.server.pm.pkg.component.ParsedPermissionGroup
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
-import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Rule
-import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyLong
@@ -56,7 +53,7 @@ import org.mockito.ArgumentMatchers.anyLong
* Mocking unit test for AppIdPermissionPolicy.
*/
@RunWith(AndroidJUnit4::class)
-open class BaseAppIdPermissionPolicyTest {
+abstract class BaseAppIdPermissionPolicyTest {
protected lateinit var oldState: MutableAccessState
protected lateinit var newState: MutableAccessState
@@ -80,7 +77,7 @@ open class BaseAppIdPermissionPolicyTest {
.build()
@Before
- open fun setUp() {
+ fun baseSetUp() {
oldState = MutableAccessState()
createUserState(USER_ID_0)
oldState.mutateExternalState().setPackageStates(ArrayMap())
@@ -139,78 +136,6 @@ open class BaseAppIdPermissionPolicyTest {
}
}
- @Test
- fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
- val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
- )
- val requestingPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
- )
- addPackageState(permissionOwnerPackageState)
- addPackageState(requestingPackageState)
- addPermission(parsedPermission)
- setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)
-
- mutateState {
- with(appIdPermissionPolicy) {
- onAppIdRemoved(APP_ID_1)
- }
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
- " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
- " flags $actualFlags should be null"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageRemoved_packageIsRemoved_permissionsAreTrimmedAndStatesAreEvaluated() {
- // TODO
- // shouldn't reuse test cases because it's really different despite it's also for
- // trim permission states. It's different because it's package removal
- }
-
- @Test
- fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
- // TODO
- // should be fine for it to be its own test cases and not to re-use
- // clearRestrictedPermissionImplicitExemption
- }
-
- @Test
- fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
- // TODO
- }
-
- @Test
- fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
- // TODO
- }
-
- @Test
- fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
- // TODO
- }
-
- @Test
- fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
- // TODO
- }
-
- @Test
- fun testOnStateMutated_notEmpty_isCalledForEachListener() {
- // TODO
- }
-
/**
* Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
*/
@@ -221,6 +146,15 @@ open class BaseAppIdPermissionPolicyTest {
permissions = listOf(defaultPermissionTree, defaultPermission)
)
+ protected fun createSimplePermission(isTree: Boolean = false): Permission {
+ val parsedPermission = if (isTree) { defaultPermissionTree } else { defaultPermission }
+ val permissionInfo = PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
+ return Permission(permissionInfo, true, Permission.TYPE_MANIFEST, APP_ID_0)
+ }
+
protected inline fun mutateState(action: MutateStateScope.() -> Unit) {
newState = oldState.toMutable()
MutateStateScope(oldState, newState).action()
@@ -330,15 +264,26 @@ open class BaseAppIdPermissionPolicyTest {
) {
state.mutateExternalState().apply {
setPackageStates(
- packageStates.toMutableMap().apply {
- put(packageState.packageName, packageState)
- }
+ packageStates.toMutableMap().apply { put(packageState.packageName, packageState) }
)
mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
.add(packageState.packageName)
}
}
+ protected fun removePackageState(
+ packageState: PackageState,
+ state: MutableAccessState = oldState
+ ) {
+ state.mutateExternalState().apply {
+ setPackageStates(
+ packageStates.toMutableMap().apply { remove(packageState.packageName) }
+ )
+ mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
+ .remove(packageState.packageName)
+ }
+ }
+
protected fun addDisabledSystemPackageState(
packageState: PackageState,
state: MutableAccessState = oldState
@@ -429,6 +374,7 @@ open class BaseAppIdPermissionPolicyTest {
@JvmStatic protected val PERMISSION_NAME_0 = "permissionName0"
@JvmStatic protected val PERMISSION_NAME_1 = "permissionName1"
@JvmStatic protected val PERMISSION_NAME_2 = "permissionName2"
+ @JvmStatic protected val PERMISSION_BELONGS_TO_A_TREE = "permissionTree.permission"
@JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE =
Manifest.permission.READ_EXTERNAL_STORAGE
@JvmStatic protected val PERMISSION_POST_NOTIFICATIONS =
diff --git a/services/tests/RemoteProvisioningServiceTests/OWNERS b/services/tests/RemoteProvisioningServiceTests/OWNERS
index 348f94048311..ea6dc727c7b2 100644
--- a/services/tests/RemoteProvisioningServiceTests/OWNERS
+++ b/services/tests/RemoteProvisioningServiceTests/OWNERS
@@ -1 +1 @@
-file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
+file:platform/frameworks/base:main:/core/java/android/security/rkp/OWNERS
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index a23539e37409..2396905aecbf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -75,6 +75,7 @@ import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -87,6 +88,7 @@ import android.media.projection.IMediaProjectionManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
@@ -118,11 +120,11 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import com.google.common.truth.Expect;
-
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -198,9 +200,10 @@ public class DisplayManagerServiceTest {
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
return new LocalDisplayAdapter(syncRoot, context, handler,
- displayAdapterListener, new LocalDisplayAdapter.Injector() {
+ displayAdapterListener, flags, new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
@@ -244,8 +247,14 @@ public class DisplayManagerServiceTest {
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener,
+ DisplayManagerFlags flags) {
+ return new LocalDisplayAdapter(
+ syncRoot,
+ context,
+ handler,
+ displayAdapterListener,
+ flags,
new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
@@ -2445,6 +2454,86 @@ public class DisplayManagerServiceTest {
assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED,
EVENT_DISPLAY_DISCONNECTED);
}
+
+ @Test
+ public void testRegisterDisplayOffloader_whenEnabled_DisplayHasDisplayOffloadSession() {
+ when(mMockFlags.isDisplayOffloadEnabled()).thenReturn(true);
+ // set up DisplayManager
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ // set up display
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.DEFAULT_DISPLAY);
+ initDisplayPowerController(localService);
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+
+ // Register DisplayOffloader.
+ DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
+ localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
+
+ assertThat(display.getDisplayOffloadSessionLocked().getDisplayOffloader()).isEqualTo(
+ mockDisplayOffloader);
+ }
+
+ @Test
+ public void testRegisterDisplayOffloader_whenDisabled_DisplayHasNoDisplayOffloadSession() {
+ when(mMockFlags.isDisplayOffloadEnabled()).thenReturn(false);
+ // set up DisplayManager
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ // set up display
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.DEFAULT_DISPLAY);
+ initDisplayPowerController(localService);
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+
+ // Register DisplayOffloader.
+ DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
+ localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
+
+ assertThat(display.getDisplayOffloadSessionLocked()).isNull();
+ }
+
+ private void initDisplayPowerController(DisplayManagerInternal localService) {
+ localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
+ @Override
+ public void onStateChanged() {
+
+ }
+
+ @Override
+ public void onProximityPositive() {
+
+ }
+
+ @Override
+ public void onProximityNegative() {
+
+ }
+
+ @Override
+ public void onDisplayStateChange(boolean allInactive, boolean allOff) {
+
+ }
+
+ @Override
+ public void acquireSuspendBlocker(String id) {
+
+ }
+
+ @Override
+ public void releaseSuspendBlocker(String id) {
+
+ }
+ }, new Handler(Looper.getMainLooper()), mSensorManager);
+ }
+
private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index a56b59a4a481..dca69eb6dd98 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -44,6 +44,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -123,6 +124,9 @@ public final class DisplayPowerController2Test {
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
+ private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@Mock
@@ -1409,6 +1413,111 @@ public final class DisplayPowerController2Test {
BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int unSupportedTargetState = Display.STATE_ON;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
+ // set up.
+ int initState = Display.STATE_OFF;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with OFF.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ private void initDisplayOffloadSession() {
+ mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
+ @Override
+ public boolean startOffload() {
+ return true;
+ }
+
+ @Override
+ public void stopOffload() {}
+ });
+
+ mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {}
+
+ @Override
+ public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
+ return mDisplayOffloader;
+ }
+ };
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -1742,9 +1851,9 @@ public final class DisplayPowerController2Test {
BrightnessRangeController getBrightnessRangeController(
HighBrightnessModeController hbmController, Runnable modeChangeCallback,
DisplayDeviceConfig displayDeviceConfig, Handler handler,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
return new BrightnessRangeController(hbmController, modeChangeCallback,
- displayDeviceConfig, mHdrClamper, mFlags);
+ displayDeviceConfig, mHdrClamper, mFlags, displayToken, info);
}
@Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 05721174b71f..edaa1d5013bf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -44,6 +44,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -122,6 +123,8 @@ public final class DisplayPowerControllerTest {
private Handler mHandler;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
+ private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+ private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
@Mock
private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -1364,6 +1367,110 @@ public final class DisplayPowerControllerTest {
verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
}
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
+ // set up.
+ int initState = Display.STATE_DOZE;
+ int unSupportedTargetState = Display.STATE_ON;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with DOZE.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ @Test
+ public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
+ // set up.
+ int initState = Display.STATE_OFF;
+ int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ doAnswer(invocation -> {
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+ return null;
+ }).when(mHolder.displayPowerState).setScreenState(anyInt());
+ // init displayoffload session and support offloading.
+ initDisplayOffloadSession();
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+ // start with OFF.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+ }
+
+ private void initDisplayOffloadSession() {
+ mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
+ @Override
+ public boolean startOffload() {
+ return true;
+ }
+
+ @Override
+ public void stopOffload() {}
+ });
+
+ mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {}
+
+ @Override
+ public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
+ return mDisplayOffloader;
+ }
+ };
+ }
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 6cde5e363657..147e8f22aab6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -32,12 +32,15 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -55,6 +58,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -72,6 +76,7 @@ import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -107,8 +112,15 @@ public class LocalDisplayAdapterTest {
private LightsManager mMockedLightsManager;
@Mock
private LogicalLight mMockedBacklight;
+ @Mock
+ private DisplayManagerFlags mFlags;
+
private Handler mHandler;
+ private DisplayOffloadSession mDisplayOffloadSession;
+
+ private DisplayOffloader mDisplayOffloader;
+
private TestListener mListener = new TestListener();
private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>();
@@ -120,6 +132,8 @@ public class LocalDisplayAdapterTest {
private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
private static final int[] BACKLIGHT_RANGE = { 1, 255 };
private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
+ private static final List<Integer> mDisplayOffloadSupportedStates
+ = new ArrayList<>(List.of(Display.STATE_DOZE_SUSPEND));
@Before
public void setUp() throws Exception {
@@ -134,7 +148,7 @@ public class LocalDisplayAdapterTest {
mInjector = new Injector();
when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
- mListener, mInjector);
+ mListener, mFlags, mInjector);
spyOn(mAdapter);
doReturn(mMockedContext).when(mAdapter).getOverlayContext();
@@ -185,6 +199,8 @@ public class LocalDisplayAdapterTest {
when(mMockedResources.getIntArray(
com.android.internal.R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
.thenReturn(new int[]{});
+ doReturn(true).when(mFlags).isDisplayOffloadEnabled();
+ initDisplayOffloadSession();
}
@After
@@ -1109,6 +1125,72 @@ public class LocalDisplayAdapterTest {
assertThat(info.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP).isEqualTo(0);
}
+
+ @Test
+ public void test_displayStateToSupportedState_DisplayOffloadStart()
+ throws InterruptedException {
+ // prepare a display.
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ for (Integer supportedState : mDisplayOffloadSupportedStates) {
+ Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(
+ supportedState, 0, 0, mDisplayOffloadSession);
+ changeStateRunnable.run();
+
+ verify(mDisplayOffloader).startOffload();
+ }
+ }
+
+ @Test
+ public void test_displayStateToDozeFromDozeSuspend_DisplayOffloadStop()
+ throws InterruptedException {
+ // prepare a display.
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ Runnable changeStateToDozeSuspendRunnable = displayDevice.requestDisplayStateLocked(
+ Display.STATE_DOZE_SUSPEND, 0, 0, mDisplayOffloadSession);
+ Runnable changeStateToDozeRunnable = displayDevice.requestDisplayStateLocked(
+ Display.STATE_DOZE, 0, 0, mDisplayOffloadSession);
+ changeStateToDozeSuspendRunnable.run();
+ changeStateToDozeRunnable.run();
+
+ verify(mDisplayOffloader).stopOffload();
+ }
+
+ private void initDisplayOffloadSession() {
+ mDisplayOffloader = spy(new DisplayOffloader() {
+ @Override
+ public boolean startOffload() {
+ return true;
+ }
+
+ @Override
+ public void stopOffload() {}
+ });
+
+ mDisplayOffloadSession = new DisplayOffloadSession() {
+ @Override
+ public void setDozeStateOverride(int displayState) {}
+
+ @Override
+ public DisplayOffloader getDisplayOffloader() {
+ return mDisplayOffloader;
+ }
+ };
+ }
+
private void setupCutoutAndRoundedCorners() {
String sampleCutout = "M 507,66\n"
+ "a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index 37d966d044c5..c3322eca6104 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -20,6 +20,12 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
import android.os.PowerManager;
import androidx.test.filters.SmallTest;
@@ -31,6 +37,7 @@ import com.android.server.testutils.TestHandler;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -40,7 +47,20 @@ import java.util.Map;
@SmallTest
public class HdrClamperTest {
- public static final float FLOAT_TOLERANCE = 0.0001f;
+ private static final float FLOAT_TOLERANCE = 0.0001f;
+ private static final long SEND_TIME_TOLERANCE = 100;
+
+ private static final HdrBrightnessData TEST_HDR_DATA = new HdrBrightnessData(
+ Map.of(500f, 0.6f),
+ /* brightnessIncreaseDebounceMillis= */ 1000,
+ /* brightnessIncreaseDurationMillis= */ 2000,
+ /* brightnessDecreaseDebounceMillis= */ 3000,
+ /* brightnessDecreaseDurationMillis= */4000
+ );
+
+ private static final int WIDTH = 600;
+ private static final int HEIGHT = 800;
+ private static final float MIN_HDR_PERCENT = 0.5f;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -48,17 +68,31 @@ public class HdrClamperTest {
@Mock
private BrightnessClamperController.ClamperChangeListener mMockListener;
+ @Mock
+ private IBinder mMockBinder;
+
+ @Mock
+ private HdrClamper.Injector mMockInjector;
+
+ @Mock
+ private HdrClamper.HdrLayerInfoListener mMockHdrInfoListener;
+
OffsettableClock mClock = new OffsettableClock.Stopped();
private final TestHandler mTestHandler = new TestHandler(null, mClock);
private HdrClamper mHdrClamper;
-
+ private HdrClamper.HdrListener mHdrChangeListener;
@Before
public void setUp() {
- mHdrClamper = new HdrClamper(mMockListener, mTestHandler);
+ when(mMockInjector.getHdrListener(any(), any())).thenReturn(mMockHdrInfoListener);
+ mHdrClamper = new HdrClamper(mMockListener, mTestHandler, mMockInjector);
+ ArgumentCaptor<HdrClamper.HdrListener> listenerCaptor = ArgumentCaptor.forClass(
+ HdrClamper.HdrListener.class);
+ verify(mMockInjector).getHdrListener(listenerCaptor.capture(), eq(mTestHandler));
+ mHdrChangeListener = listenerCaptor.getValue();
configureClamper();
}
@@ -68,20 +102,23 @@ public class HdrClamperTest {
assertFalse(mTestHandler.hasMessagesOrCallbacks());
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
- public void testClamper_AmbientLuxChangesBelowLimit() {
+ public void testClamper_AmbientLuxChangesBelowLimit_MaxDecrease() {
mHdrClamper.onAmbientLuxChange(499);
assertTrue(mTestHandler.hasMessagesOrCallbacks());
TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
- assertEquals(2000, msgInfo.sendTime);
+ assertSendTime(3000, msgInfo.sendTime);
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
- mClock.fastForward(2000);
+ mClock.fastForward(3000);
mTestHandler.timeAdvance();
assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 4
}
@Test
@@ -91,33 +128,65 @@ public class HdrClamperTest {
assertFalse(mTestHandler.hasMessagesOrCallbacks());
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
public void testClamper_AmbientLuxChangesBelowLimit_ThenSlowlyAboveLimit() {
mHdrClamper.onAmbientLuxChange(499);
- mClock.fastForward(2000);
+ mClock.fastForward(3000);
mTestHandler.timeAdvance();
mHdrClamper.onAmbientLuxChange(500);
assertTrue(mTestHandler.hasMessagesOrCallbacks());
TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
- assertEquals(3000, msgInfo.sendTime); // 2000 + 1000
+ assertSendTime(4000, msgInfo.sendTime); // 3000 + 1000
mClock.fastForward(1000);
mTestHandler.timeAdvance();
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0.2f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 2
+ }
+
+ @Test
+ public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit() {
+ mHdrChangeListener.onHdrVisible(false);
+ mHdrClamper.onAmbientLuxChange(499);
+
+ assertFalse(mTestHandler.hasMessagesOrCallbacks());
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit_ThenHdrOn() {
+ mHdrChangeListener.onHdrVisible(false);
+ mHdrClamper.onAmbientLuxChange(499);
+ mHdrChangeListener.onHdrVisible(true);
+
+ assertTrue(mTestHandler.hasMessagesOrCallbacks());
+ TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+ assertSendTime(3000, msgInfo.sendTime);
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+
+ mClock.fastForward(3000);
+ mTestHandler.timeAdvance();
+ assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+ assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ }
+
+ // MsgInfo.sendTime is calculated first by adding SystemClock.uptimeMillis()
+ // (in Handler.sendMessageDelayed) and then by subtracting SystemClock.uptimeMillis()
+ // (in TestHandler.sendMessageAtTime, there might be several milliseconds difference between
+ // SystemClock.uptimeMillis() calls, and subtracted value might be greater than added.
+ private static void assertSendTime(long expectedTime, long sendTime) {
+ assertTrue(expectedTime >= sendTime);
+ assertTrue(expectedTime - SEND_TIME_TOLERANCE < sendTime);
}
private void configureClamper() {
- HdrBrightnessData data = new HdrBrightnessData(
- Map.of(500f, 0.6f),
- /* brightnessIncreaseDebounceMillis= */ 1000,
- /* brightnessIncreaseDurationMillis= */ 1500,
- /* brightnessDecreaseDebounceMillis= */ 2000,
- /* brightnessDecreaseDurationMillis= */2500
- );
- mHdrClamper.resetHdrConfig(data);
+ mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
+ mHdrChangeListener.onHdrVisible(true);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index fbad369ab19e..b8c18e070397 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -91,6 +91,7 @@ import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.TestUtils;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.sensors.SensorManagerInternal;
@@ -110,7 +111,9 @@ import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -121,10 +124,114 @@ import junitparams.Parameters;
@SmallTest
@RunWith(JUnitParamsRunner.class)
public class DisplayModeDirectorTest {
- // The tolerance within which we consider something approximately equals.
+ public static Collection<Object[]> getAppRequestedSizeTestCases() {
+ var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
+ {DEFAULT_MODE_75.getModeId(), Float.POSITIVE_INFINITY,
+ DEFAULT_MODE_75.getRefreshRate(), Map.of()},
+ {APP_MODE_HIGH_90.getModeId(), Float.POSITIVE_INFINITY,
+ APP_MODE_HIGH_90.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+ APP_MODE_HIGH_90.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()))},
+ {LIMIT_MODE_70.getModeId(), Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+ APP_MODE_HIGH_90.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight()))},
+ {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
+ LIMIT_MODE_70.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight()))},
+ {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
+ LIMIT_MODE_70.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ 0, 0,
+ LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight(),
+ 0, Float.POSITIVE_INFINITY)), false},
+ {APP_MODE_65.getModeId(), APP_MODE_65.getRefreshRate(),
+ APP_MODE_65.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ 0, 0,
+ LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight(),
+ 0, Float.POSITIVE_INFINITY)), true}});
+
+ final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2);
+
+ // Add additional argument for displayResolutionRangeVotingEnabled=false if not present.
+ for (var testCaseArrayArgs : appRequestedSizeTestCases) {
+ if (testCaseArrayArgs.length == 4) {
+ var testCaseListArgs = new ArrayList<>(Arrays.asList(testCaseArrayArgs));
+ testCaseListArgs.add(/* displayResolutionRangeVotingEnabled */ false);
+ res.add(testCaseListArgs.toArray());
+ } else {
+ res.add(testCaseArrayArgs);
+ }
+ }
+
+ // Add additional argument for displayResolutionRangeVotingEnabled=true if not present.
+ for (var testCaseArrayArgs : appRequestedSizeTestCases) {
+ if (testCaseArrayArgs.length == 4) {
+ var testCaseListArgs = new ArrayList<>(Arrays.asList(testCaseArrayArgs));
+ testCaseListArgs.add(/* displayResolutionRangeVotingEnabled */ true);
+ res.add(testCaseListArgs.toArray());
+ }
+ }
+
+ return res;
+ }
+
private static final String TAG = "DisplayModeDirectorTest";
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
+
+ private static final Display.Mode APP_MODE_65 = new Display.Mode(
+ /*modeId=*/65, /*width=*/1900, /*height=*/1900, 65);
+ private static final Display.Mode LIMIT_MODE_70 = new Display.Mode(
+ /*modeId=*/70, /*width=*/2000, /*height=*/2000, 70);
+ private static final Display.Mode DEFAULT_MODE_75 = new Display.Mode(
+ /*modeId=*/75, /*width=*/2500, /*height=*/2500, 75);
+ private static final Display.Mode APP_MODE_HIGH_90 = new Display.Mode(
+ /*modeId=*/90, /*width=*/3000, /*height=*/3000, 90);
+ private static final Display.Mode[] TEST_MODES = new Display.Mode[] {
+ new Display.Mode(
+ /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60),
+ APP_MODE_65,
+ LIMIT_MODE_70,
+ DEFAULT_MODE_75,
+ APP_MODE_HIGH_90
+ };
+
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
@@ -142,6 +249,8 @@ public class DisplayModeDirectorTest {
public SensorManagerInternal mSensorManagerInternalMock;
@Mock
public DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
@Before
public void setUp() throws Exception {
@@ -177,7 +286,7 @@ public class DisplayModeDirectorTest {
private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
Display.Mode defaultMode) {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector);
+ new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
director.setLoggingEnabled(true);
SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
supportedModesByDisplay.put(DISPLAY_ID, modes);
@@ -219,9 +328,8 @@ public class DisplayModeDirectorTest {
// should take precedence over lower priority votes.
{
int minFps = 60;
- int maxFps = 90;
- director = createDirectorFromFpsRange(60, 90);
- assertTrue(2 * numPriorities < maxFps - minFps + 1);
+ int maxFps = minFps + 2 * numPriorities;
+ director = createDirectorFromFpsRange(minFps, maxFps);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
@@ -472,6 +580,7 @@ public class DisplayModeDirectorTest {
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
}
+ /** Resolution range voting disabled */
@Test
public void testAppRequestRefreshRateRange() {
// Confirm that the app request range doesn't include flicker or min refresh rate settings,
@@ -530,6 +639,33 @@ public class DisplayModeDirectorTest {
assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
}
+ /** Tests for app requested size */
+ @Parameters(method = "getAppRequestedSizeTestCases")
+ @Test
+ public void testAppRequestedSize(final int expectedBaseModeId,
+ final float expectedPhysicalRefreshRate,
+ final float expectedAppRequestedRefreshRate,
+ final Map<Integer, Vote> votesWithPriorities,
+ final boolean displayResolutionRangeVotingEnabled) {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled())
+ .thenReturn(displayResolutionRangeVotingEnabled);
+ DisplayModeDirector director = createDirectorFromModeArray(TEST_MODES, DEFAULT_MODE_75);
+
+ SparseArray<Vote> votes = new SparseArray<>();
+ votesWithPriorities.forEach(votes::put);
+
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ director.injectVotesByDisplay(votesByDisplay);
+
+ var desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(expectedBaseModeId);
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.primary.physical.max).isAtLeast(expectedPhysicalRefreshRate);
+ assertThat(desiredSpecs.appRequest.physical.min).isAtMost(0);
+ assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(expectedAppRequestedRefreshRate);
+ }
+
void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps,
float peakFps, float defaultFps, RefreshRateRanges primary,
RefreshRateRanges appRequest) {
@@ -843,7 +979,7 @@ public class DisplayModeDirectorTest {
@Test
public void testStaleAppRequestSize() {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector);
+ new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
Display.Mode[] modes = new Display.Mode[] {
new Display.Mode(1, 1280, 720, 60),
};
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
new file mode 100644
index 000000000000..ff91d34470d4
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
+
+import static com.android.server.display.mode.DisplayModeDirector.SYNCHRONIZED_REFRESH_RATE_TOLERANCE;
+import static com.android.server.display.mode.Vote.PRIORITY_LIMIT_MODE;
+import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE;
+import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE;
+import static com.android.server.display.mode.VotesStorage.GLOBAL_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.DeviceConfigInterface;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.sensors.SensorManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import junitparams.JUnitParamsRunner;
+
+
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class DisplayObserverTest {
+ private static final int EXTERNAL_DISPLAY = 1;
+ private static final int MAX_WIDTH = 1920;
+ private static final int MAX_HEIGHT = 1080;
+ private static final int MAX_REFRESH_RATE = 60;
+
+ private final Display.Mode[] mInternalDisplayModes = new Display.Mode[] {
+ new Display.Mode(/*modeId=*/ 0, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ (float) MAX_REFRESH_RATE / 2),
+ new Display.Mode(/*modeId=*/ 1, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 2, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 3, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 4, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 5, MAX_WIDTH * 2, MAX_HEIGHT * 2,
+ MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 6, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE * 3),
+ };
+
+ private final Display.Mode[] mExternalDisplayModes = new Display.Mode[] {
+ new Display.Mode(/*modeId=*/ 0, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ (float) MAX_REFRESH_RATE / 2),
+ new Display.Mode(/*modeId=*/ 1, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 2, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 3, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 4, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 5, MAX_WIDTH * 2, MAX_HEIGHT * 2,
+ MAX_REFRESH_RATE),
+ };
+
+ private DisplayModeDirector mDmd;
+ private Context mContext;
+ private DisplayModeDirector.Injector mInjector;
+ private Handler mHandler;
+ private DisplayManager.DisplayListener mObserver;
+ private Resources mResources;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
+ private int mExternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ private int mInternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ private Display mDefaultDisplay;
+ private Display mExternalDisplay;
+
+ /** Setup tests. */
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(Looper.getMainLooper());
+ mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mResources = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(0);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(0);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(0);
+ when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
+ .thenReturn(false);
+
+ // Necessary configs to initialize DisplayModeDirector
+ when(mResources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{5});
+ when(mResources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{10});
+ when(mResources.getIntArray(
+ R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{250});
+ when(mResources.getIntArray(
+ R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{7000});
+ }
+
+ /** No vote for user preferred mode */
+ @Test
+ public void testExternalDisplay_notVotedUserPreferredMode() {
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is not added when display is added because feature is disabled
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is not present after display is removed
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is not added when display is changed because feature is disabled
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Vote for user preferred mode */
+ @Test
+ public void testExternalDisplay_voteUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedVote = Vote.forSize(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ mExternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ preferredMode = mExternalDisplayModes[4];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ expectedVote = Vote.forSize(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ // Testing that the vote is removed.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** External display: Do not apply limit to user preferred mode */
+ @Test
+ public void testExternalDisplay_doNotApplyLimitToUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedResolutionVote = Vote.forSize(preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedResolutionVote);
+
+ // Testing that the vote is removed.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Default display: Do not apply limit to user preferred mode */
+ @Test
+ public void testDefaultDisplayAdded_notAppliedLimitToUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+ var preferredMode = mInternalDisplayModes[5];
+ mInternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedResolutionVote = Vote.forSize(preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedResolutionVote);
+ mObserver.onDisplayRemoved(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Default display added, no mode limit set */
+ @Test
+ public void testDefaultDisplayAdded() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+ init();
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayAdded(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ }
+
+ /** External display added, apply resolution refresh rate limit */
+ @Test
+ public void testExternalDisplayAdded_applyResolutionRefreshRateLimit() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(
+ Vote.forSizeAndPhysicalRefreshRatesRange(0, 0,
+ MAX_WIDTH, MAX_HEIGHT,
+ /*minPhysicalRefreshRate=*/ 0, MAX_REFRESH_RATE));
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ }
+
+ /** External display added, disabled resolution refresh rate limit. */
+ @Test
+ public void testExternalDisplayAdded_disabledResolutionRefreshRateLimit() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ }
+
+ /** External display added, applied refresh rates synchronization */
+ @Test
+ public void testExternalDisplayAdded_appliedRefreshRatesSynchronization() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
+ .thenReturn(true);
+ init();
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(
+ Vote.forPhysicalRefreshRates(
+ MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+ MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+
+ // Remove external display and check that sync vote is no longer present.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ }
+
+ /** External display added, disabled feature refresh rates synchronization */
+ @Test
+ public void testExternalDisplayAdded_disabledFeatureRefreshRatesSynchronization() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
+ .thenReturn(true);
+ init();
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ }
+
+ /** External display not applied refresh rates synchronization, because
+ * config_refreshRateSynchronizationEnabled is false. */
+ @Test
+ public void testExternalDisplay_notAppliedRefreshRatesSynchronization() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
+ init();
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ }
+
+ private void init() {
+ mInjector = mock(DisplayModeDirector.Injector.class);
+ doAnswer(invocation -> {
+ assertThat(mObserver).isNull();
+ mObserver = invocation.getArgument(0);
+ return null;
+ }).when(mInjector).registerDisplayListener(any(), any());
+
+ doAnswer(c -> {
+ DisplayInfo info = c.getArgument(1);
+ info.type = Display.TYPE_INTERNAL;
+ info.displayId = DEFAULT_DISPLAY;
+ info.defaultModeId = 0;
+ info.supportedModes = mInternalDisplayModes;
+ info.userPreferredModeId = mInternalDisplayUserPreferredModeId;
+ return true;
+ }).when(mInjector).getDisplayInfo(eq(DEFAULT_DISPLAY), /*displayInfo=*/ any());
+
+ doAnswer(c -> {
+ DisplayInfo info = c.getArgument(1);
+ info.type = Display.TYPE_EXTERNAL;
+ info.displayId = EXTERNAL_DISPLAY;
+ info.defaultModeId = 0;
+ info.supportedModes = mExternalDisplayModes;
+ info.userPreferredModeId = mExternalDisplayUserPreferredModeId;
+ return true;
+ }).when(mInjector).getDisplayInfo(eq(EXTERNAL_DISPLAY), /*displayInfo=*/ any());
+
+ doAnswer(c -> mock(SensorManagerInternal.class)).when(mInjector).getSensorManagerInternal();
+ doAnswer(c -> mock(DeviceConfigInterface.class)).when(mInjector).getDeviceConfig();
+
+ mDefaultDisplay = mock(Display.class);
+ when(mDefaultDisplay.getDisplayId()).thenReturn(DEFAULT_DISPLAY);
+ doAnswer(c -> mInjector.getDisplayInfo(DEFAULT_DISPLAY, c.getArgument(0)))
+ .when(mDefaultDisplay).getDisplayInfo(/*displayInfo=*/ any());
+
+ mExternalDisplay = mock(Display.class);
+ when(mExternalDisplay.getDisplayId()).thenReturn(EXTERNAL_DISPLAY);
+ doAnswer(c -> mInjector.getDisplayInfo(EXTERNAL_DISPLAY, c.getArgument(0)))
+ .when(mExternalDisplay).getDisplayInfo(/*displayInfo=*/ any());
+
+ when(mInjector.getDisplays()).thenReturn(new Display[] {mDefaultDisplay, mExternalDisplay});
+
+ mDmd = new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ mDmd.start(null);
+ assertThat(mObserver).isNotNull();
+ }
+
+ @Nullable
+ private Vote getVote(final int displayId, final int priority) {
+ return mDmd.getVote(displayId, priority);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 287fdd5c344b..50e239218fa0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -19,6 +19,7 @@ package com.android.server.display.mode;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.util.SparseArray;
@@ -72,6 +73,18 @@ public class VotesStorageTest {
verify(mVotesListener).onChanged();
}
+ /** Verifies that adding the same vote twice results in a single call to onChanged */
+ @Test
+ public void notifiesVoteListenerCalledOnceIfVoteUpdatedTwice() {
+ // WHEN updateVote is called
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE);
+ // THEN listener is notified, but only when vote changes.
+ verify(mVotesListener, times(3)).onChanged();
+ }
+
@Test
public void addsAnotherVoteToStorageWithDifferentPriority() {
// GIVEN vote storage with one vote
diff --git a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
index 880501f39ac2..f5c6bb2e618b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
@@ -141,6 +141,32 @@ public final class DisplayStateControllerTest {
assertEquals(false, mDisplayStateController.shouldPerformScreenOffTransition());
}
+ @Test
+ public void dozeScreenStateOverrideToDozeSuspend_DozePolicy_updateDisplayStateToDozeSuspend() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ new DisplayManagerInternal.DisplayPowerRequest();
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ mDisplayStateController.overrideDozeScreenState(Display.STATE_DOZE_SUSPEND);
+
+ int state = mDisplayStateController.updateDisplayState(displayPowerRequest, DISPLAY_ENABLED,
+ !DISPLAY_IN_TRANSITION);
+
+ assertEquals(state, Display.STATE_DOZE_SUSPEND);
+ }
+
+ @Test
+ public void dozeScreenStateOverrideToDozeSuspend_OffPolicy_displayRemainOff() {
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ new DisplayManagerInternal.DisplayPowerRequest();
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+ mDisplayStateController.overrideDozeScreenState(Display.STATE_DOZE_SUSPEND);
+
+ int state = mDisplayStateController.updateDisplayState(displayPowerRequest, DISPLAY_ENABLED,
+ !DISPLAY_IN_TRANSITION);
+
+ assertEquals(state, Display.STATE_OFF);
+ }
+
private void validDisplayState(int policy, int displayState, boolean isEnabled,
boolean isInTransition) {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 52044bfaef24..de8b3080907c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -117,7 +117,9 @@ public class ApexManagerTest {
Build.VERSION_CODES.CUR_DEVELOPMENT,
Build.VERSION.INCREMENTAL);
mMockSystem.system().validateFinalState();
- mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class));
+ mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class),
+ mock(RemovePackageHelper.class), mock(DeletePackageHelper.class),
+ mock(BroadcastHelper.class));
}
@NonNull
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index d6a4d40c763c..931b38dc2951 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -34,6 +34,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
@RunWith(JUnit4::class)
class DeletePackageHelperTest {
@@ -79,7 +80,8 @@ class DeletePackageHelperTest {
whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1, 0, false)
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
@@ -97,7 +99,8 @@ class DeletePackageHelperTest {
whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
UserInfo(userId, "testparent", 0))
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, userId, 0, false)
assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
@@ -112,7 +115,8 @@ class DeletePackageHelperTest {
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_DENIED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1,
PackageManager.DELETE_SYSTEM_APP, false)
@@ -133,7 +137,8 @@ class DeletePackageHelperTest {
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_DENIED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, userId,
PackageManager.DELETE_SYSTEM_APP, false)
@@ -150,7 +155,8 @@ class DeletePackageHelperTest {
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_DENIED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1,
PackageManager.DELETE_SYSTEM_APP, false)
@@ -164,7 +170,8 @@ class DeletePackageHelperTest {
whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
.thenReturn(PERMISSION_GRANTED)
- val dph = DeletePackageHelper(mPms)
+ val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+ mock(BroadcastHelper::class.java))
val result = dph.deletePackageX("a.data.package", 1L, 1,
PackageManager.DELETE_SYSTEM_APP, false)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index 9f1cec3b595a..cf81f0a77702 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -39,7 +39,7 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
override fun setup() {
super.setup()
distractingPackageHelper = DistractingPackageHelper(
- pms, rule.mocks().injector, broadcastHelper, suspendPackageHelper)
+ pms, broadcastHelper, suspendPackageHelper)
}
@Test
@@ -50,12 +50,11 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
+ pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
- val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS)
+ val modifiedPackages = pkgListCaptor.value
+ val distractionFlags = flagsCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
assertThat(unactionedPackages).isEmpty()
@@ -75,10 +74,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid)
testHandler.flush()
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper, never()).sendPackageBroadcast(
- eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
- nullable())
+ verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+ any(), any(), any(), any(), any())
assertThat(unactionedPackages).isEmpty()
}
@@ -154,11 +151,11 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS)
+ verify(broadcastHelper).sendDistractingPackagesChanged(
+ any(Computer::class.java), pkgListCaptor.capture(), any(), eq(TEST_USER_ID),
+ flagsCaptor.capture())
+ val modifiedPackages = pkgListCaptor.value
+ val distractionFlags = flagsCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_NONE)
}
@@ -170,9 +167,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper, never()).sendPackageBroadcast(eq(
- Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
- nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+ any(), any(), any(), any(), any())
}
@Test
@@ -189,22 +185,21 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
arrayOfNulls(0), TEST_USER_ID)
testHandler.flush()
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper, never()).sendPackageBroadcast(eq(
- Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
- nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+ any(), any(), any(), any(), any())
}
@Test
fun sendDistractingPackagesChanged() {
- distractingPackageHelper.sendDistractingPackagesChanged(packagesToChange, uidsToChange,
- TEST_USER_ID, PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
+ broadcastHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+ packagesToChange, uidsToChange, TEST_USER_ID,
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
testHandler.flush()
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
+ pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
- var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ var changedPackages = pkgListCaptor.value
+ var changedUids = uidsCaptor.value
assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(changedUids).asList().containsExactly(
packageSetting1.appId, packageSetting2.appId)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index 5fd270ecb2b4..eb001645863f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -17,7 +17,6 @@
package com.android.server.pm
import android.os.Build
-import android.os.Bundle
import android.os.UserHandle
import android.os.UserManager
import com.android.server.pm.pkg.PackageStateInternal
@@ -68,7 +67,11 @@ open class PackageHelperTestBase {
lateinit var protectedPackages: ProtectedPackages
@Captor
- lateinit var bundleCaptor: ArgumentCaptor<Bundle>
+ lateinit var pkgListCaptor: ArgumentCaptor<Array<String>>
+ @Captor
+ lateinit var flagsCaptor: ArgumentCaptor<Int>
+ @Captor
+ lateinit var uidsCaptor: ArgumentCaptor<IntArray>
@Rule
@JvmField
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 c5db5db41787..6c44fd0da1c4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -17,12 +17,12 @@
package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.Intent;
import android.content.pm.PackageInstaller;
@@ -63,9 +63,7 @@ public class PackageMonitorCallbackHelperTest {
@Before
public void setup() {
- when(mMockSystem.mocks().getInjector().getHandler()).thenReturn(mHandler);
- mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(
- mMockSystem.mocks().getInjector());
+ mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper();
}
@@ -80,7 +78,7 @@ public class PackageMonitorCallbackHelperTest {
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -93,7 +91,7 @@ public class PackageMonitorCallbackHelperTest {
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
- null /* broadcastAllowList */);
+ null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
@@ -101,7 +99,7 @@ public class PackageMonitorCallbackHelperTest {
mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -114,7 +112,7 @@ public class PackageMonitorCallbackHelperTest {
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -138,7 +136,7 @@ public class PackageMonitorCallbackHelperTest {
// Notify for user 10
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -155,7 +153,7 @@ public class PackageMonitorCallbackHelperTest {
mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME,
false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */,
new int[]{0} /* userIds */, null /* instantUserIds */,
- null /* broadcastAllowList */);
+ null /* broadcastAllowList */, mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -183,7 +181,8 @@ public class PackageMonitorCallbackHelperTest {
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME,
FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0], false /* isArchived */,
- PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */);
+ PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */,
+ mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -207,7 +206,7 @@ public class PackageMonitorCallbackHelperTest {
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */,
true /* replacing */, new String[]{FAKE_PACKAGE_NAME},
- new int[]{FAKE_PACKAGE_UID} /* uids */);
+ new int[]{FAKE_PACKAGE_UID} /* uids */, mHandler);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -240,7 +239,7 @@ public class PackageMonitorCallbackHelperTest {
mPackageMonitorCallbackHelper.onUserRemoved(10);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
- null /* instantUserIds */, null /* broadcastAllowList */);
+ null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -256,7 +255,7 @@ public class PackageMonitorCallbackHelperTest {
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList);
+ null /* instantUserIds */, broadcastAllowList, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
}
@@ -272,7 +271,7 @@ public class PackageMonitorCallbackHelperTest {
Binder.getCallingUid());
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList);
+ null /* instantUserIds */, broadcastAllowList, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
}
@@ -288,7 +287,7 @@ public class PackageMonitorCallbackHelperTest {
Process.SYSTEM_UID);
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
- null /* instantUserIds */, broadcastAllowList);
+ null /* instantUserIds */, broadcastAllowList, mHandler);
verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 679757629e32..4240373b7c1d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -22,12 +22,11 @@ import android.os.Binder
import android.os.PersistableBundle
import com.android.server.testutils.any
import com.android.server.testutils.eq
-import com.android.server.testutils.nullable
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -44,17 +43,10 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
- nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), nullable(),
- nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
- nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), nullable(),
- nullable(), nullable())
-
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+ eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
+
+ var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(failedNames).isEmpty()
}
@@ -146,6 +138,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
+ Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
targetPackages, false /* suspended */, null /* appExtras */,
@@ -154,17 +147,13 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
- nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
- nullable(), nullable(), nullable())
-
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+ eq(Intent.ACTION_PACKAGES_UNSUSPENDED), pkgListCaptor.capture(), any(), any(),
+ any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
+ any(), any(), any())
+
+ var modifiedPackages = pkgListCaptor.value
assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
assertThat(failedNames).isEmpty()
}
@@ -206,7 +195,7 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
assertThat(failedNames).isEmpty()
- val result = suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ val result = SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)!!
assertThat(result.getString(TEST_PACKAGE_1)).isEqualTo(TEST_PACKAGE_1)
@@ -222,14 +211,15 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
false /* forQuietMode */, false /* quarantined */)
testHandler.flush()
+ Mockito.clearInvocations(broadcastHelper)
assertThat(failedNames).isEmpty()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
@@ -238,23 +228,18 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
testHandler.flush()
verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
- verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
- nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
- nullable(), nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
- nullable(), nullable(), nullable())
- verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
- nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
- nullable(), nullable(), nullable())
+ verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+ eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
+ verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
+ any(), any(), any())
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
- assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+ assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
}
@@ -319,39 +304,4 @@ class SuspendPackageHelperTest : PackageHelperTestBase() {
assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
}
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendedForUser() {
- suspendPackageHelper.sendPackagesSuspendedForUser(
- Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, false, TEST_USER_ID)
- testHandler.flush()
- verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
- nullable())
-
- var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(changedUids).asList().containsExactly(
- packageSetting1.appId, packageSetting2.appId)
- }
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendModifiedForUser() {
- suspendPackageHelper.sendPackagesSuspendedForUser(
- Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, false, TEST_USER_ID)
- testHandler.flush()
- verify(broadcastHelper).sendPackageBroadcast(
- eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
- nullable())
-
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(modifiedUids).asList().containsExactly(
- packageSetting1.appId, packageSetting2.appId)
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 82b75408ad18..2f0257ab6b25 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -39,11 +39,16 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
+import android.view.WindowManager;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -76,6 +81,9 @@ public class AccessibilityServiceConnectionTest {
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
AccessibilityServiceConnection mConnection;
@Mock AccessibilityUserState mMockUserState;
@@ -113,6 +121,8 @@ public class AccessibilityServiceConnectionTest {
when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
+ when(mMockContext.getSystemService(Context.DISPLAY_SERVICE))
+ .thenReturn(new DisplayManager(mMockContext));
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -168,6 +178,18 @@ public class AccessibilityServiceConnectionTest {
assertFalse(mConnection.getServiceInfo().crashed);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void onServiceConnected_addsWindowTokens() {
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+ verify(mMockWindowManagerInternal).addWindowToken(
+ any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+ anyInt(), eq(null));
+ }
+
private void setServiceBinding(ComponentName componentName) {
when(mMockUserState.getBindingServicesLocked())
.thenReturn(new HashSet<>(Arrays.asList(componentName)));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b4558b211fc6..63281b77ade7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -35,6 +35,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -46,6 +47,9 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
@@ -88,6 +92,9 @@ public class AccessibilityUserStateTest {
@Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock private AccessibilityServiceInfo mMockServiceInfo;
@Mock private AccessibilityServiceConnection mMockConnection;
@@ -188,7 +195,7 @@ public class AccessibilityUserStateTest {
mUserState.addServiceLocked(mMockConnection);
- verify(mMockConnection, never()).onAdded();
+ verify(mMockListener, never()).onServiceInfoChangedLocked(any());
}
@Test
@@ -197,13 +204,24 @@ public class AccessibilityUserStateTest {
mUserState.addServiceLocked(mMockConnection);
- verify(mMockConnection).onAdded();
assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
}
@Test
+ // addServiceLocked only calls addWindowTokensForAllDisplays when
+ // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on.
+ @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void addService_flagDisabled_addsWindowTokens() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection).addWindowTokensForAllDisplays();
+ }
+
+ @Test
public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
// When soft kb show mode is hidden in settings and is auto in state.
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 4ce9ba031b25..3ee5f61bb8cd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -21,8 +21,11 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
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.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +39,10 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -43,6 +50,7 @@ import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -58,6 +66,9 @@ public class UiAutomationManagerTest {
MessageCapturingHandler mMessageCapturingHandler;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@@ -197,6 +208,24 @@ public class UiAutomationManagerTest {
assertEquals(0, mUiAutomationManager.getRequestedEventMaskLocked());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void registerUiAutomationService_callsAddWindowTokenUsingHandler() {
+ register(0);
+ // registerUiTestAutomationServiceLocked() should not directly call addWindowToken.
+ verify(mMockWindowManagerInternal, never()).addWindowToken(
+ any(), anyInt(), anyInt(), any());
+
+ // Advance UiAutomationManager#UiAutomationService's handler.
+ mMessageCapturingHandler.sendAllMessages();
+
+ // After advancing the handler we expect addWindowToken to have been called
+ // by the UiAutomationService instance.
+ verify(mMockWindowManagerInternal).addWindowToken(
+ any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+ anyInt(), eq(null));
+ }
+
private void register(int flags) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
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 a0bca3b6b9fe..24ad976f6e45 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
@@ -50,7 +50,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.platform.test.annotations.FlakyTest;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.InputDevice;
@@ -60,6 +59,7 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.FlakyTest;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
new file mode 100644
index 000000000000..a7c8a6cee0d9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -0,0 +1,890 @@
+/*
+ * 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.companion.virtual;
+
+import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.WindowConfiguration;
+import android.companion.virtual.IVirtualDeviceIntentInterceptor;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.ArraySet;
+import android.view.Display;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.app.BlockedAppStreamingActivity;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class GenericWindowPolicyControllerTest {
+
+ private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
+ private static final int TEST_UID = 1234567;
+ private static final String DISPLAY_CATEGORY = "com.display.category";
+ private static final String NONBLOCKED_APP_PACKAGE_NAME = "com.someapp";
+ private static final String BLOCKED_PACKAGE_NAME = "com.blockedapp";
+ private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000;
+ private static final String TEST_SITE = "http://test";
+ private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
+ new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+ private static final ComponentName BLOCKED_COMPONENT = new ComponentName(BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME);
+ private static final ComponentName NONBLOCKED_COMPONENT = new ComponentName(
+ NONBLOCKED_APP_PACKAGE_NAME, NONBLOCKED_APP_PACKAGE_NAME);
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Mock
+ private GenericWindowPolicyController.PipBlockedCallback mPipBlockedCallback;
+ @Mock
+ private VirtualDeviceManager.ActivityListener mActivityListener;
+ @Mock
+ private GenericWindowPolicyController.IntentListenerCallback mIntentListenerCallback;
+ @Mock
+ private GenericWindowPolicyController.ActivityBlockedCallback mActivityBlockedCallback;
+ @Mock
+ private GenericWindowPolicyController.RunningAppsChangedListener mRunningAppsChangedListener;
+ @Mock
+ private GenericWindowPolicyController.SecureWindowCallback mSecureWindowCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void showTasksInHostDeviceRecents() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.setShowInHostDeviceRecents(true);
+ assertThat(gwpc.canShowTasksInHostDeviceRecents()).isTrue();
+
+ gwpc.setShowInHostDeviceRecents(false);
+ assertThat(gwpc.canShowTasksInHostDeviceRecents()).isFalse();
+ }
+
+ @Test
+ public void containsUid() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ assertThat(gwpc.containsUid(TEST_UID)).isFalse();
+
+ gwpc.onRunningAppsChanged(new ArraySet<>(Arrays.asList(TEST_UID)));
+ assertThat(gwpc.containsUid(TEST_UID)).isTrue();
+
+ gwpc.onRunningAppsChanged(new ArraySet<>());
+ assertThat(gwpc.containsUid(TEST_UID)).isFalse();
+ }
+
+ @Test
+ public void isEnteringPipAllowed_falseByDefault() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isFalse();
+ verify(mPipBlockedCallback).onEnteringPipBlocked(TEST_UID);
+ }
+
+ @Test
+ public void isEnteringPipAllowed_dpcSupportsPinned_allowed() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setSupportedWindowingModes(new HashSet<>(
+ Arrays.asList(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+ WindowConfiguration.WINDOWING_MODE_PINNED)));
+ assertThat(gwpc.isEnteringPipAllowed(TEST_UID)).isTrue();
+ verify(mPipBlockedCallback, never()).onEnteringPipBlocked(TEST_UID);
+ }
+
+ @Test
+ public void openNonBlockedAppOnVirtualDisplay_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void activityDoesNotSupportDisplayOnRemoteDevices_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ false,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void openBlockedComponentOnVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void addActivityPolicyExemption_openBlockedOnVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(true);
+ gwpc.addActivityPolicyExemption(BLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void openNotAllowedComponentOnBlocklistVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void addActivityPolicyExemption_openNotAllowedOnVirtualDisplay_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(false);
+ gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void openAllowedComponentOnBlocklistVirtualDisplay_startsActivity() {
+ GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void addActivityPolicyExemption_openAllowedOnVirtualDisplay_startsActivity() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(false);
+ gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_mismatchingUserHandle_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null,
+ /* uid */ UserHandle.PER_USER_RANGE + 1);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponent_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentExplicitlyBlocked_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithBlockedComponent(
+ BLOCKED_APP_STREAMING_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentExemptFromStreaming_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(true);
+ gwpc.addActivityPolicyExemption(BLOCKED_APP_STREAMING_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentNotAllowlisted_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_blockedAppStreamingComponentNotExemptFromBlocklist_isNeverBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+ gwpc.setActivityLaunchDefaultAllowed(false);
+ gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+ BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_customDisplayCategoryMatches_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ DISPLAY_CATEGORY);
+
+ assertActivityCanBeLaunched(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_customDisplayCategoryDoesNotMatch_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ "some.random.category");
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunch_fromDefaultDisplay_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, Display.DEFAULT_DISPLAY, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notExplicitlyBlocked_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor(
+ BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertActivityCanBeLaunched(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_explicitlyBlocked_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationBlockedFor(
+ BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_notAllowed_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed(
+ NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_crossTaskLaunchFromVirtualDisplay_allowed_isNotBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithCrossTaskNavigationAllowed(
+ NONBLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityCanBeLaunched(gwpc, DISPLAY_ID, true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_unsupportedWindowingMode_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, true, WindowConfiguration.WINDOWING_MODE_PINNED,
+ activityInfo);
+ }
+
+ @Test
+ public void canActivityBeLaunched_permissionComponent_isBlocked() {
+ GenericWindowPolicyController gwpc = createGwpcWithPermissionComponent(BLOCKED_COMPONENT);
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ BLOCKED_PACKAGE_NAME,
+ BLOCKED_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertActivityIsBlocked(gwpc, activityInfo);
+ }
+
+ @Test
+ public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
+ ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.registerRunningAppsChangedListener(mRunningAppsChangedListener);
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1);
+ verify(mRunningAppsChangedListener).onRunningAppsChanged(uids);
+ }
+
+ @Test
+ public void onRunningAppsChanged_empty_onDisplayEmpty() {
+ ArraySet<Integer> uids = new ArraySet<>();
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+ verify(mActivityListener).onDisplayEmpty(DISPLAY_ID);
+ }
+
+ @Test
+ public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
+ ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+ verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids);
+ }
+
+ @Test
+ public void registerUnregisterRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
+ ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(TEST_UID));
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.registerRunningAppsChangedListener(mRunningAppsChangedListener);
+ gwpc.unregisterRunningAppsChangedListener(mRunningAppsChangedListener);
+ gwpc.onRunningAppsChanged(uids);
+
+ assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
+ verify(mRunningAppsChangedListener, never()).onRunningAppsChanged(uids);
+ }
+
+ @Test
+ public void canActivityBeLaunched_intentInterceptedWhenRegistered_activityNoLaunch()
+ throws RemoteException {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_SITE));
+
+ IVirtualDeviceIntentInterceptor.Stub interceptor =
+ mock(IVirtualDeviceIntentInterceptor.Stub.class);
+ doNothing().when(interceptor).onIntentIntercepted(any());
+ doReturn(interceptor).when(interceptor).asBinder();
+ doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
+
+ GenericWindowPolicyController gwpc = createGwpc();
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ // register interceptor and intercept intent
+ when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isFalse();
+
+ // unregister interceptor and launch activity
+ when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
+ public void canActivityBeLaunched_noMatchIntentFilter_activityLaunches() {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("testing"));
+
+ GenericWindowPolicyController gwpc = createGwpc();
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ // register interceptor with different filter
+ when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
+ .isTrue();
+ verify(mIntentListenerCallback).shouldInterceptIntent(any(Intent.class));
+ }
+
+ @Test
+ public void onTopActivitychanged_null_noCallback() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ gwpc.onTopActivityChanged(null, 0, 0);
+ verify(mActivityListener, never())
+ .onTopActivityChanged(anyInt(), any(ComponentName.class), anyInt());
+ }
+
+ @Test
+ public void onTopActivitychanged_activityListenerCallbackObserved() {
+ int userId = 1000;
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ gwpc.onTopActivityChanged(BLOCKED_COMPONENT, 0, userId);
+ verify(mActivityListener)
+ .onTopActivityChanged(eq(DISPLAY_ID), eq(BLOCKED_COMPONENT), eq(userId));
+ }
+
+ @Test
+ public void keepActivityOnWindowFlagsChanged_noChange() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
+
+ verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID,
+ activityInfo.applicationInfo.uid);
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ }
+
+ @Test
+ public void keepActivityOnWindowFlagsChanged_flagSecure_isAllowedAfterTM() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue();
+
+ verify(mSecureWindowCallback).onSecureWindowShown(DISPLAY_ID,
+ activityInfo.applicationInfo.uid);
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ }
+
+ @Test
+ public void keepActivityOnWindowFlagsChanged_systemFlagHideNonSystemOverlayWindows_isAllowedAfterTM() {
+ GenericWindowPolicyController gwpc = createGwpc();
+ gwpc.setDisplayId(DISPLAY_ID);
+
+ ActivityInfo activityInfo = getActivityInfo(
+ NONBLOCKED_APP_PACKAGE_NAME,
+ NONBLOCKED_APP_PACKAGE_NAME,
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue();
+
+ verify(mSecureWindowCallback, never()).onSecureWindowShown(DISPLAY_ID,
+ activityInfo.applicationInfo.uid);
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(DISPLAY_ID, activityInfo);
+ }
+
+ @Test
+ public void getCustomHomeComponent_noneSet() {
+ GenericWindowPolicyController gwpc = createGwpc();
+
+ assertThat(gwpc.getCustomHomeComponent()).isNull();
+ }
+
+ @Test
+ public void getCustomHomeComponent_returnsHomeComponent() {
+ GenericWindowPolicyController gwpc = createGwpcWithCustomHomeComponent(
+ NONBLOCKED_COMPONENT);
+
+ assertThat(gwpc.getCustomHomeComponent()).isEqualTo(NONBLOCKED_COMPONENT);
+ }
+
+ private GenericWindowPolicyController createGwpc() {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ mSecureWindowCallback,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithCustomHomeComponent(
+ ComponentName homeComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ homeComponent);
+ }
+
+ private GenericWindowPolicyController createGwpcWithBlockedComponent(
+ ComponentName blockedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ Collections.singleton(blockedComponent),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithAllowedComponent(
+ ComponentName allowedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ false,
+ /* activityPolicyExemptions= */ Collections.singleton(allowedComponent),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithDisplayCategory(
+ String displayCategory) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ Collections.singleton(displayCategory),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithCrossTaskNavigationBlockedFor(
+ ComponentName blockedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ true,
+ /* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithCrossTaskNavigationAllowed(
+ ComponentName allowedComponent) {
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ false,
+ /* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent),
+ /* permissionDialogComponent= */ null,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private GenericWindowPolicyController createGwpcWithPermissionComponent(
+ ComponentName permissionComponent) {
+ //TODO instert the component
+ return new GenericWindowPolicyController(
+ 0,
+ 0,
+ /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
+ /* activityLaunchAllowedByDefault= */ true,
+ /* activityPolicyExemptions= */ new ArraySet<>(),
+ /* crossTaskNavigationAllowedByDefault= */ false,
+ /* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent= */ permissionComponent,
+ /* activityListener= */ mActivityListener,
+ /* pipBlockedCallback= */ mPipBlockedCallback,
+ /* activityBlockedCallback= */ mActivityBlockedCallback,
+ /* secureWindowCallback= */ null,
+ /* intentListenerCallback= */ mIntentListenerCallback,
+ /* displayCategories= */ new ArraySet<>(),
+ /* showTasksInHostDeviceRecents= */ true,
+ /* customHomeComponent= */ null);
+ }
+
+ private Set<UserHandle> getCurrentUserId() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ return new ArraySet<>(Arrays.asList(context.getUser()));
+ }
+
+ private ActivityInfo getActivityInfo(
+ String packageName, String name, boolean displayOnRemoteDevices,
+ String requiredDisplayCategory) {
+ return getActivityInfo(packageName, name, displayOnRemoteDevices, requiredDisplayCategory,
+ 0);
+ }
+
+ private ActivityInfo getActivityInfo(
+ String packageName, String name, boolean displayOnRemoteDevices,
+ String requiredDisplayCategory, int uid) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.uid = uid;
+
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = packageName;
+ activityInfo.name = name;
+ activityInfo.flags = displayOnRemoteDevices
+ ? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
+ activityInfo.applicationInfo = applicationInfo;
+ activityInfo.requiredDisplayCategory = requiredDisplayCategory;
+ return activityInfo;
+ }
+
+ private void assertActivityCanBeLaunched(GenericWindowPolicyController gwpc,
+ ActivityInfo activityInfo) {
+ assertActivityCanBeLaunched(gwpc, DISPLAY_ID, false,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ private void assertActivityCanBeLaunched(GenericWindowPolicyController gwpc, int fromDisplay,
+ boolean isNewTask, int windowingMode, ActivityInfo activityInfo) {
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
+ isNewTask)).isTrue();
+
+ verify(mActivityBlockedCallback, never()).onActivityBlocked(fromDisplay, activityInfo);
+ verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ }
+
+ private void assertActivityIsBlocked(GenericWindowPolicyController gwpc,
+ ActivityInfo activityInfo) {
+ assertActivityIsBlocked(gwpc, DISPLAY_ID, false,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, activityInfo);
+ }
+
+ private void assertActivityIsBlocked(GenericWindowPolicyController gwpc, int fromDisplay,
+ boolean isNewTask, int windowingMode, ActivityInfo activityInfo) {
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
+ isNewTask)).isFalse();
+
+ verify(mActivityBlockedCallback).onActivityBlocked(fromDisplay, activityInfo);
+ verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 41af9e31dbdd..263d4701eba1 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -24,6 +24,7 @@ import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.content.Context.DEVICE_ID_INVALID;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
+import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -57,6 +58,7 @@ import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorCallback;
@@ -69,6 +71,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
@@ -100,6 +103,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
@@ -211,6 +215,9 @@ public class VirtualDeviceManagerServiceTest {
private static final String TEST_SITE = "http://test";
@Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Rule
public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
Manifest.permission.CREATE_VIRTUAL_DEVICE);
@@ -298,7 +305,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices= */ true,
+ /* displayOnRemoteDevices= */ true,
targetDisplayCategory);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -309,12 +316,12 @@ public class VirtualDeviceManagerServiceTest {
private ActivityInfo getActivityInfo(
- String packageName, String name, boolean displayOnRemoveDevices,
+ String packageName, String name, boolean displayOnRemoteDevices,
String requiredDisplayCategory) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = packageName;
activityInfo.name = name;
- activityInfo.flags = displayOnRemoveDevices
+ activityInfo.flags = displayOnRemoteDevices
? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
activityInfo.applicationInfo = mApplicationInfoMock;
activityInfo.requiredDisplayCategory = requiredDisplayCategory;
@@ -328,6 +335,11 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+ mSetFlagsRule.disableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_DYNAMIC_POLICY);
+ mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_VDM_CUSTOM_HOME);
+
doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
@@ -1417,7 +1429,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1438,7 +1450,51 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
PERMISSION_CONTROLLER_PACKAGE_NAME,
PERMISSION_CONTROLLER_PACKAGE_NAME,
- /* displayOnRemoveDevices */ false,
+ /* displayOnRemoteDevices */ false,
+ /* targetDisplayCategory */ null);
+ Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
+ activityInfo, mAssociationInfo.getDisplayName());
+ gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false);
+
+ verify(mContext).startActivityAsUser(argThat(intent ->
+ intent.filterEquals(blockedAppIntent)), any(), any());
+ }
+
+ @Test
+ public void openPermissionControllerOnVirtualDisplay_displayOnRemoteDevices_startsWhenFlagIsEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+ DISPLAY_ID_1);
+ doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+
+ ActivityInfo activityInfo = getActivityInfo(
+ PERMISSION_CONTROLLER_PACKAGE_NAME,
+ PERMISSION_CONTROLLER_PACKAGE_NAME,
+ /* displayOnRemoveDevices */ true,
+ /* targetDisplayCategory */ null);
+ Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
+ activityInfo, mAssociationInfo.getDisplayName());
+ gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false);
+
+ verify(mContext, never()).startActivityAsUser(argThat(intent ->
+ intent.filterEquals(blockedAppIntent)), any(), any());
+ }
+
+ @Test
+ public void openPermissionControllerOnVirtualDisplay_dontDisplayOnRemoteDevices_startsWhenFlagIsEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+ DISPLAY_ID_1);
+ doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+
+ ActivityInfo activityInfo = getActivityInfo(
+ PERMISSION_CONTROLLER_PACKAGE_NAME,
+ PERMISSION_CONTROLLER_PACKAGE_NAME,
+ /* displayOnRemoveDevices */ false,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1459,7 +1515,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
SETTINGS_PACKAGE_NAME,
SETTINGS_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1480,7 +1536,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
VENDING_PACKAGE_NAME,
VENDING_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1501,7 +1557,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
GOOGLE_DIALER_PACKAGE_NAME,
GOOGLE_DIALER_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1522,7 +1578,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
GOOGLE_MAPS_PACKAGE_NAME,
GOOGLE_MAPS_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
activityInfo, mAssociationInfo.getDisplayName());
@@ -1562,6 +1618,54 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void canActivityBeLaunched_permissionDialog_flagDisabled_isBlocked() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ VirtualDeviceParams params = new VirtualDeviceParams.Builder().build();
+ mDeviceImpl.close();
+ mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
+ doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+ DISPLAY_ID_1);
+ ComponentName permissionComponent = getPermissionDialogComponent();
+ ActivityInfo activityInfo = getActivityInfo(
+ permissionComponent.getPackageName(),
+ permissionComponent.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+ .isFalse();
+
+ Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
+ activityInfo, mAssociationInfo.getDisplayName());
+ verify(mContext).startActivityAsUser(argThat(intent ->
+ intent.filterEquals(blockedAppIntent)), any(), any());
+ }
+
+ @Test
+ public void canActivityBeLaunched_permissionDialog_flagEnabled_isStreamed() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS);
+ VirtualDeviceParams params = new VirtualDeviceParams.Builder().build();
+ mDeviceImpl.close();
+ mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
+
+ addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
+ GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
+ DISPLAY_ID_1);
+ ComponentName permissionComponent = getPermissionDialogComponent();
+ ActivityInfo activityInfo = getActivityInfo(
+ permissionComponent.getPackageName(),
+ permissionComponent.getClassName(),
+ /* displayOnRemoteDevices */ true,
+ /* targetDisplayCategory */ null);
+ assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+ .isTrue();
+ }
+
+ @Test
public void canActivityBeLaunched_activityCanLaunch() {
Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
@@ -1570,7 +1674,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
@@ -1594,7 +1698,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
@@ -1637,7 +1741,7 @@ public class VirtualDeviceManagerServiceTest {
ActivityInfo activityInfo = getActivityInfo(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
- /* displayOnRemoveDevices */ true,
+ /* displayOnRemoteDevices */ true,
/* targetDisplayCategory */ null);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
@@ -1758,6 +1862,13 @@ public class VirtualDeviceManagerServiceTest {
NONBLOCKED_APP_PACKAGE_NAME);
}
+ private ComponentName getPermissionDialogComponent() {
+ Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+ PackageManager packageManager = mContext.getPackageManager();
+ intent.setPackage(packageManager.getPermissionControllerPackageName());
+ return intent.resolveActivity(packageManager);
+ }
+
/** Helper class to drop permissions temporarily and restore them at the end of a test. */
static final class DropShellPermissionsTemporarily implements AutoCloseable {
DropShellPermissionsTemporarily() {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
index c65452aa2fa1..07dd59d2e2d8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
@@ -32,11 +32,12 @@ import android.companion.virtual.VirtualDevice;
import android.companion.virtual.flags.Flags;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -49,6 +50,10 @@ public class VirtualDeviceTest {
private static final int VIRTUAL_DEVICE_ID = 42;
private static final String PERSISTENT_ID = "persistentId";
private static final String DEVICE_NAME = "VirtualDeviceName";
+ private static final String DISPLAY_NAME = "DisplayName";
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
private IVirtualDevice mVirtualDevice;
@@ -87,7 +92,8 @@ public class VirtualDeviceTest {
@Test
public void parcelable_shouldRecreateSuccessfully() {
VirtualDevice originalDevice =
- new VirtualDevice(mVirtualDevice, VIRTUAL_DEVICE_ID, PERSISTENT_ID, DEVICE_NAME);
+ new VirtualDevice(mVirtualDevice, VIRTUAL_DEVICE_ID, PERSISTENT_ID, DEVICE_NAME,
+ DISPLAY_NAME);
Parcel parcel = Parcel.obtain();
originalDevice.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
@@ -96,11 +102,13 @@ public class VirtualDeviceTest {
assertThat(device.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID);
assertThat(device.getPersistentDeviceId()).isEqualTo(PERSISTENT_ID);
assertThat(device.getName()).isEqualTo(DEVICE_NAME);
+ assertThat(device.getDisplayName().toString()).isEqualTo(DISPLAY_NAME);
}
- @RequiresFlagsEnabled(Flags.FLAG_VDM_PUBLIC_APIS)
@Test
public void virtualDevice_getDisplayIds() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+
VirtualDevice virtualDevice =
new VirtualDevice(
mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
@@ -113,9 +121,10 @@ public class VirtualDeviceTest {
assertThat(virtualDevice.getDisplayIds()).isEqualTo(displayIds);
}
- @RequiresFlagsEnabled(Flags.FLAG_VDM_PUBLIC_APIS)
@Test
public void virtualDevice_hasCustomSensorSupport() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+
VirtualDevice virtualDevice =
new VirtualDevice(
mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index 1c48b8aa79f9..ef5270e8f003 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -82,6 +82,7 @@ public class VirtualAudioControllerTest {
/* activityPolicyExemptions= */ new ArraySet<>(),
/* crossTaskNavigationAllowedByDefault= */ true,
/* crossTaskNavigationExemptions= */ new ArraySet<>(),
+ /* permissionDialogComponent */ null,
/* activityListener= */ null,
/* pipBlockedCallback= */ null,
/* activityBlockedCallback= */ null,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index dee77806e4f3..37a6d22f038b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -781,7 +781,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.onCleanupUser(PRIMARY_USER_ID);
+ mService.onUserStopped(PRIMARY_USER_ID);
assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
index 2c9ba34e850f..e8b7ad7e7389 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
@@ -169,7 +169,7 @@ public class WeakEscrowTokenTests extends BaseLockSettingsServiceTests{
assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
- mService.onCleanupUser(PRIMARY_USER_ID);
+ mService.onUserStopped(PRIMARY_USER_ID);
assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 020afdbce987..8d7b5cb984b4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -141,6 +141,7 @@ import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.protobuf.InvalidProtocolBufferException;
import org.json.JSONArray;
@@ -563,7 +564,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -647,7 +648,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -2620,6 +2621,16 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testOnlyHasDefaultChannel() throws Exception {
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+ UID_N_MR1, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -2644,16 +2655,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testOnlyHasDefaultChannel() throws Exception {
- assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
- assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
-
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
- UID_N_MR1, false);
- assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
- }
-
- @Test
public void testCreateChannel_defaultChannelId() throws Exception {
try {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
@@ -2884,7 +2885,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
UID_N_MR1});
assertEquals(0, mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList().size());
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList().size());
}
@Test
@@ -3022,7 +3023,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
@@ -3056,14 +3057,15 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel1.setGroup(ncg.getId());
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
UID_N_MR1, false);
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false, true, null)
+ .getList();
channel1.setImportance(IMPORTANCE_LOW);
mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -3089,7 +3091,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, false, true).getList();
+ PKG_N_MR1, UID_N_MR1, false, false, true, true, null).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -5786,6 +5788,62 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertFalse(isUserSet);
}
+ @Test
+ public void testGetNotificationChannelGroups_withChannelFilter_includeBlocked() {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+ UID_N_MR1, false);
+ // modifying same object, don't need to call updateNotificationChannel
+ channel.setImportance(IMPORTANCE_NONE);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+ UID_N_MR1, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+ UID_N_MR1, false);
+
+ Set<String> filter = ImmutableSet.of("id3");
+
+ NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, filter).getList().get(0);
+ assertEquals(2, actual.getChannels().size());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+ }
+
+ @Test
+ public void testGetNotificationChannelGroups_withChannelFilter_doNotIncludeBlocked() {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+ UID_N_MR1, false);
+ // modifying same object, don't need to call updateNotificationChannel
+ channel.setImportance(IMPORTANCE_NONE);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+ UID_N_MR1, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+ UID_N_MR1, false);
+
+ Set<String> filter = ImmutableSet.of("id3");
+
+ NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false, false, filter).getList().get(0);
+ assertEquals(1, actual.getChannels().size());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+ assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+ }
+
private static NotificationChannel cloneChannel(NotificationChannel original) {
Parcel parcel = Parcel.obtain();
try {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 61c4d06131e1..8db09f9e3a16 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,6 +46,7 @@ import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -98,6 +99,10 @@ class ShortcutKeyTestBase {
* settings values.
*/
protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
doReturn(mSettingsProviderRule.mockContentResolver(mContext))
.when(mContext).getContentResolver();
mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 6e2c4bd7e570..ef28ffa7da8f 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -70,6 +70,7 @@ import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
import android.media.AudioManagerInternal;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -77,7 +78,6 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import android.os.test.TestLooper;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
@@ -160,8 +160,8 @@ class TestPhoneWindowManager {
@Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
private StaticMockitoSession mMockitoSession;
+ private HandlerThread mHandlerThread;
private Handler mHandler;
- private TestLooper mTestLooper;
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
@@ -184,11 +184,12 @@ class TestPhoneWindowManager {
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
- mTestLooper = new TestLooper();
- mHandler = new Handler(mTestLooper.getLooper());
+ mHandlerThread = new HandlerThread("fake window manager");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
- mHandler.post(() -> setUp(supportSettingsUpdate));
- mTestLooper.dispatchAll();
+ mHandler.runWithScissors(() -> setUp(supportSettingsUpdate), 0 /* timeout */);
+ waitForIdle();
}
private void setUp(boolean supportSettingsUpdate) {
@@ -300,6 +301,7 @@ class TestPhoneWindowManager {
}
void tearDown() {
+ mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
@@ -326,7 +328,7 @@ class TestPhoneWindowManager {
}
void waitForIdle() {
- mTestLooper.dispatchAll();
+ mHandler.runWithScissors(() -> { }, 0 /* timeout */);
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index f65cb93258bc..40ac7b1ccdca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2855,14 +2855,14 @@ public class ActivityRecordTests extends WindowTestsBase {
.setTask(sourceRecord.getTask()).build();
secondRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, sourceRecord);
- assertTrue(secondRecord.mAllowIconSplashScreen);
+ assertFalse(secondRecord.mSplashScreenStyleSolidColor);
secondRecord.onStartingWindowDrawn();
final ActivityRecord finalRecord = new ActivityBuilder(mAtm)
.setTask(sourceRecord.getTask()).build();
finalRecord.showStartingWindow(null /* prev */, true /* newTask */, false,
true /* startActivity */, secondRecord);
- assertFalse(finalRecord.mAllowIconSplashScreen);
+ assertTrue(finalRecord.mSplashScreenStyleSolidColor);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 397681cf134e..c2b7fec4ff66 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1091,7 +1091,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(dc.getRotationReversionController().isAnyOverrideActive());
dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED,
- ROTATION_90);
+ ROTATION_90, /* caller= */ "DisplayContentTests");
updateAllDisplayContentAndRotation(dc);
assertEquals(ROTATION_90, dc.getDisplayRotation()
.rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_90));
@@ -1110,7 +1110,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(ROTATION_90, dc.getDisplayRotation()
.rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_0));
dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE,
- ROTATION_0);
+ ROTATION_0, /* caller= */ "DisplayContentTests");
}
@Test
@@ -1137,7 +1137,8 @@ public class DisplayContentTests extends WindowTestsBase {
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
- WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180);
+ WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180,
+ /* caller= */ "DisplayContentTests");
final int newOrientation = getRotatedOrientation(dc);
final Task task = new TaskBuilder(mSupervisor)
@@ -1177,7 +1178,8 @@ public class DisplayContentTests extends WindowTestsBase {
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
- WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0);
+ WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0,
+ /* caller= */ "DisplayContentTests");
dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int newOrientation = getRotatedOrientation(dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 915b387ddb75..e14568d281c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -1265,7 +1265,7 @@ public class DisplayRotationTests {
}
private void freezeRotation(int rotation) {
- mTarget.freezeRotation(rotation);
+ mTarget.freezeRotation(rotation, /* caller= */ "DisplayRotationTests");
if (mTarget.isDefaultDisplay) {
mAccelerometerRotationObserver.onChange(false);
@@ -1274,7 +1274,7 @@ public class DisplayRotationTests {
}
private void thawRotation() {
- mTarget.thawRotation();
+ mTarget.thawRotation(/* caller= */ "DisplayRotationTests");
if (mTarget.isDefaultDisplay) {
mAccelerometerRotationObserver.onChange(false);
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 25619b9ec5e8..4c25a4bf1455 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -91,6 +91,7 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Build/Install/Run:
@@ -563,6 +564,38 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
+ public void testTasksWithCorrectOrderOfLastActiveTime() {
+ mRecentTasks.setOnlyTestVisibleRange();
+ mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
+
+ // Setup some tasks for the user
+ mTaskPersister.mUserTaskIdsOverride = new SparseBooleanArray();
+ mTaskPersister.mUserTaskIdsOverride.put(1, true);
+ mTaskPersister.mUserTaskIdsOverride.put(2, true);
+ mTaskPersister.mUserTaskIdsOverride.put(3, true);
+ mTaskPersister.mUserTasksOverride = new ArrayList<>();
+ mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask1").build());
+ mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask2").build());
+ mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask3").build());
+
+ // Assert no user tasks are initially loaded
+ assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).hasLength(0);
+
+ // Load tasks
+ mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
+ assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
+
+ // Sort the time descendingly so the order should be in-sync with task recency (most
+ // recent to least recent)
+ List<Task> tasksSortedByTime = mRecentTasks.getRawTasks().stream()
+ .sorted((o1, o2) -> Long.compare(o2.lastActiveTime, o1.lastActiveTime))
+ .collect(Collectors.toList());
+
+ assertTrue("Task order is not in sync with its recency",
+ mRecentTasks.getRawTasks().equals(tasksSortedByTime));
+ }
+
+ @Test
public void testOrderedIteration() {
mRecentTasks.setOnlyTestVisibleRange();
Task task1 = createTaskBuilder(".Task1").build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index fb27d6368a7e..0c580697bc4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -610,7 +610,7 @@ public class TaskTests extends WindowTestsBase {
// display.
final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0, /* caller= */ "TaskTests");
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
@@ -642,7 +642,7 @@ public class TaskTests extends WindowTestsBase {
// Setting app to fixed landscape and changing display
top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
// Fix the display orientation to portrait which is 90 degrees for the test display.
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90, /* caller= */ "TaskTests");
// Fixed orientation request should be resolved on activity level. Task fills display
// bounds.
@@ -681,7 +681,7 @@ public class TaskTests extends WindowTestsBase {
// display.
final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0, /* caller= */ "TaskTests");
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 36a8fc1c43a0..c8f890c2c6ba 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -18,6 +18,7 @@ package android.telephony;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,6 +53,7 @@ import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
import com.android.internal.telephony.ICarrierConfigLoader;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.util.List;
@@ -9436,22 +9438,9 @@ public class CarrierConfigManager {
* </carrier_config>
* }</pre>
* <p>
- * If this carrier config is not present, the device overlay config
- * {@code config_satellite_services_supported_by_providers} will be used. If the carrier config
- * is present, the supported services associated with the PLMNs listed in the carrier config
- * will override that of the device overlay config. The supported satellite services will be
- * identified as follows:
- * <ul>
- * <li>For each PLMN that exists only in the carrier provided satellite services, use the
- * carrier provided services as the supported services.</li>
- * <li>For each PLMN that is present only in the device provided satellite services, use the
- * device provided services as the supported services.</li>
- * <li>For each PLMN that is present in both the carrier provided and device provided satellite
- * services, use the carrier provided services as the supported services.</li>
- * </ul>
- * <p>
* This config is empty by default.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
"carrier_supported_satellite_services_per_provider_bundle";
@@ -9462,9 +9451,8 @@ public class CarrierConfigManager {
* satellite provider and the carrier before enabling this flag.
*
* The default value is false.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL =
"satellite_attach_supported_bool";
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index a4527f12a199..8dc2de8178c4 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -85,29 +85,11 @@ public final class SatelliteManager {
@Nullable private final Context mContext;
/**
- * Create a new SatelliteManager object pinned to the given subscription ID.
- * This is needed only to handle carrier specific satellite features.
- * For eg: requestSatelliteAttachEnabledForCarrier and
- * requestIsSatelliteAttachEnabledForCarrier
- *
- * @return a SatelliteManager that uses the given subId for all satellite activities.
- * @throws IllegalArgumentException if the subscription is invalid.
- * @hide
- */
- public SatelliteManager createForSubscriptionId(int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- throw new IllegalArgumentException("Invalid subscription ID");
- }
- return new SatelliteManager(mContext, subId);
- }
-
- /**
* Create an instance of the SatelliteManager.
*
* @param context The context the SatelliteManager belongs to.
* @hide
*/
-
public SatelliteManager(@Nullable Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
@@ -846,8 +828,8 @@ public final class SatelliteManager {
/**
* Satellite communication restricted by geolocation. This can be
* triggered based upon geofence input provided by carrier to enable or disable satellite.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;
/** @hide */
@@ -1643,26 +1625,28 @@ public final class SatelliteManager {
* {@code true}.</li>
* </ul>
*
+ * @param subId The subscription ID of the carrier.
* @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestSatelliteAttachEnabledForCarrier(boolean enableSatellite,
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void requestSatelliteAttachEnabledForCarrier(int subId, boolean enableSatellite,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(resultListener);
if (enableSatellite) {
- removeSatelliteAttachRestrictionForCarrier(
+ removeSatelliteAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
} else {
- addSatelliteAttachRestrictionForCarrier(
+ addSatelliteAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
}
}
@@ -1671,6 +1655,7 @@ public final class SatelliteManager {
* Request to get whether the carrier supported satellite plmn scan and attach by modem is
* enabled by user.
*
+ * @param subId The subscription ID of the carrier.
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
@@ -1681,16 +1666,17 @@ public final class SatelliteManager {
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestIsSatelliteAttachEnabledForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void requestIsSatelliteAttachEnabledForCarrier(int subId,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
- Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier();
+ Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier(subId);
executor.execute(() -> callback.onResult(
!restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)));
}
@@ -1699,19 +1685,25 @@ public final class SatelliteManager {
* Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
* by modem.
*
+ * @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void addSatelliteAttachRestrictionForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void addSatelliteAttachRestrictionForCarrier(int subId,
@SatelliteCommunicationRestrictionReason int reason,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -1722,7 +1714,7 @@ public final class SatelliteManager {
() -> resultListener.accept(result)));
}
};
- telephony.addSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+ telephony.addSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1736,19 +1728,25 @@ public final class SatelliteManager {
* Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
* by modem.
*
+ * @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void removeSatelliteAttachRestrictionForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void removeSatelliteAttachRestrictionForCarrier(int subId,
@SatelliteCommunicationRestrictionReason int reason,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -1759,7 +1757,7 @@ public final class SatelliteManager {
() -> resultListener.accept(result)));
}
};
- telephony.removeSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+ telephony.removeSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1773,34 +1771,40 @@ public final class SatelliteManager {
* Get reasons for disallowing satellite attach, as requested by
* {@link #addSatelliteAttachRestrictionForCarrier(int, Executor, Consumer)}
*
+ * @param subId The subscription ID of the carrier.
* @return Set of reasons for disallowing satellite communication.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteCommunicationRestrictionReason
- public @NonNull Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier() {
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @NonNull public Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
int[] receivedArray =
- telephony.getSatelliteAttachRestrictionReasonsForCarrier(mSubId);
+ telephony.getSatelliteAttachRestrictionReasonsForCarrier(subId);
if (receivedArray.length == 0) {
- logd("received set is empty, create empty set");
+ logd("receivedArray is empty, create empty set");
return new HashSet<>();
} else {
return Arrays.stream(receivedArray).boxed().collect(Collectors.toSet());
}
} else {
- throw new IllegalStateException("telephony service is null.");
+ throw new IllegalStateException("Telephony service is null.");
}
} catch (RemoteException ex) {
loge("getSatelliteAttachRestrictionReasonsForCarrier() RemoteException: " + ex);
ex.rethrowFromSystemServer();
}
- return null;
+ return new HashSet<>();
}
private static ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 02661de34fb2..0fcd0d622d36 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -392,7 +392,11 @@ oneway interface ISatellite {
*
* @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
* this information to determine the relevant carrier.
- * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+ * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+ * supported by user subscription.
+ * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+ * PLMNs that are not supported by the carrier and make sure not to
+ * attach to them.
* @param resultCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -404,8 +408,8 @@ oneway interface ISatellite {
* SatelliteError:RADIO_NOT_AVAILABLE
* SatelliteError:REQUEST_NOT_SUPPORTED
*/
- void setSatellitePlmn(int simSlot, in List<String> plmnList,
- in IIntegerConsumer resultCallback);
+ void setSatellitePlmn(int simSlot, in List<String> carrierPlmnList,
+ in List<String> allSatellitePlmnList, in IIntegerConsumer resultCallback);
/**
* Enable or disable satellite in the cellular modem associated with a carrier.
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index 6451daf2e752..a9c09c94d57c 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -214,12 +214,12 @@ public class SatelliteImplBase extends SatelliteService {
}
@Override
- public void setSatellitePlmn(int simSlot, List<String> plmnList,
- IIntegerConsumer errorCallback)
+ public void setSatellitePlmn(int simSlot, List<String> carrierPlmnList,
+ List<String> devicePlmnList, IIntegerConsumer errorCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this
- .setSatellitePlmn(simSlot, plmnList, errorCallback),
+ () -> SatelliteImplBase.this.setSatellitePlmn(
+ simSlot, carrierPlmnList, devicePlmnList, errorCallback),
"setSatellitePlmn");
}
@@ -655,13 +655,15 @@ public class SatelliteImplBase extends SatelliteService {
* SIM profile. Acquisition of satellite based system is lower priority to terrestrial
* networks. UE shall make all attempts to acquire terrestrial service prior to camping on
* satellite LTE service.
- * This method must only take effect if {@link #setSatelliteEnabledForCarrier} is {@code true},
- * and return an error otherwise.
*
* @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
* applied. The modem will use this information to determine the relevant carrier.
* @param errorCallback The callback to receive the error code result of the operation.
- * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+ * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+ * supported by user subscription.
+ * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+ * PLMNs that are not supported by the carrier and make sure not to
+ * attach to them.
*
* Valid error codes returned:
* SatelliteError:NONE
@@ -672,7 +674,8 @@ public class SatelliteImplBase extends SatelliteService {
* SatelliteError:RADIO_NOT_AVAILABLE
* SatelliteError:REQUEST_NOT_SUPPORTED
*/
- public void setSatellitePlmn(@NonNull int simLogicalSlotIndex, @NonNull List<String> plmnList,
+ public void setSatellitePlmn(@NonNull int simLogicalSlotIndex,
+ @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
@NonNull IIntegerConsumer errorCallback) {
// stub implementation
}
diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp
new file mode 100644
index 000000000000..78b0ede76d4e
--- /dev/null
+++ b/tests/BinderLeakTest/Android.bp
@@ -0,0 +1,40 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "binder_leak_test_aidl",
+ srcs: ["**/*.aidl"],
+ path: "aidl",
+}
+
+java_defaults {
+ name: "BinderTest.defaults",
+ srcs: [
+ "**/*.java",
+ ":binder_leak_test_aidl",
+ ],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ ],
+}
+
+// Built with target_sdk_version: current
+android_test {
+ name: "BinderLeakTest",
+ defaults: ["BinderTest.defaults"],
+}
+
+// Built with target_sdk_version: 33
+android_test {
+ name: "BinderLeakTest_legacy",
+ defaults: ["BinderTest.defaults"],
+ manifest: "AndroidManifest_legacy.xml",
+}
diff --git a/tests/BinderLeakTest/AndroidManifest.xml b/tests/BinderLeakTest/AndroidManifest.xml
new file mode 100644
index 000000000000..756def7ac29d
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.binder">
+ <application>
+ <service
+ android:name=".MyService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":service">
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.binder"
+ android:label="Binder leak test">
+ </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/AndroidManifest_legacy.xml b/tests/BinderLeakTest/AndroidManifest_legacy.xml
new file mode 100644
index 000000000000..03d1dfd1fd83
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest_legacy.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.binder">
+ <uses-sdk android:minSdkVersion="33"
+ android:targetSdkVersion="33"
+ android:maxSdkVersion="33" />
+ <application>
+ <service
+ android:name=".MyService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":service">
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.binder"
+ android:label="Binder leak test">
+ </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
new file mode 100644
index 000000000000..a721959d19b4
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
@@ -0,0 +1,5 @@
+package com.android.test.binder;
+
+interface IFoo {
+
+}
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
new file mode 100644
index 000000000000..b487f51f812c
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
@@ -0,0 +1,10 @@
+package com.android.test.binder;
+import com.android.test.binder.IFoo;
+
+interface IFooProvider {
+ IFoo createFoo();
+
+ boolean isFooGarbageCollected();
+
+ oneway void killProcess();
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
new file mode 100644
index 000000000000..f07317f7d5f3
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.binder;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ServiceTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class BinderTest {
+ @Rule
+ public final ServiceTestRule serviceRule = new ServiceTestRule();
+
+ @Test
+ public void testDeathRecipientLeaksOrNot()
+ throws RemoteException, TimeoutException, InterruptedException {
+ Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class);
+ IFooProvider provider = IFooProvider.Stub.asInterface(serviceRule.bindService(intent));
+ FooHolder holder = new FooHolder(provider.createFoo());
+
+ // ref will get enqueued right after holder is finalized for gc.
+ ReferenceQueue<FooHolder> refQueue = new ReferenceQueue<>();
+ PhantomReference<FooHolder> ref = new PhantomReference<>(holder, refQueue);
+
+ DeathRecorder deathRecorder = new DeathRecorder();
+ holder.registerDeathRecorder(deathRecorder);
+
+ if (getSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ /////////////////////////////////////////////
+ // New behavior
+ //
+ // Reference chain at this moment:
+ // holder --(java strong ref)--> FooHolder
+ // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+ // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+ // BinderProxy --(binder ref)--> Foo.Stub
+ // In other words, the variable "holder" is the root of the reference chain.
+
+ // By setting the variable to null, we make FooHolder, IFoo.Proxy, BinderProxy, and even
+ // Foo.Stub unreachable.
+ holder = null;
+
+ // Ensure that the objects are garbage collected
+ forceGc();
+ assertEquals(ref, refQueue.poll());
+ assertTrue(provider.isFooGarbageCollected());
+
+ // The binder has died, but we don't get notified since the death recipient is GC'ed.
+ provider.killProcess();
+ Thread.sleep(1000); // give some time for the service process to die and reaped
+ assertFalse(deathRecorder.deathRecorded);
+ } else {
+ /////////////////////////////////////////////
+ // Legacy behavior
+ //
+ // Reference chain at this moment:
+ // JavaDeathRecipient --(JNI strong ref)--> FooHolder
+ // holder --(java strong ref)--> FooHolder
+ // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+ // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+ // BinderProxy --(binder ref)--> Foo.Stub
+ // So, BOTH JavaDeathRecipient and holder are roots of the reference chain.
+
+ // Even if we set holder to null, it doesn't make other objects unreachable; they are
+ // still reachable via the JNI strong ref.
+ holder = null;
+
+ // Check that objects are not garbage collected
+ forceGc();
+ assertNotEquals(ref, refQueue.poll());
+ assertFalse(provider.isFooGarbageCollected());
+
+ // The legacy behavior is getting notified even when there's no reference
+ provider.killProcess();
+ Thread.sleep(1000); // give some time for the service process to die and reaped
+ assertTrue(deathRecorder.deathRecorded);
+ }
+ }
+
+ static class FooHolder implements IBinder.DeathRecipient {
+ private IFoo mProxy;
+ private DeathRecorder mDeathRecorder;
+
+ FooHolder(IFoo proxy) throws RemoteException {
+ proxy.asBinder().linkToDeath(this, 0);
+
+ // A strong reference from DeathRecipient(this) to the binder proxy is created here
+ mProxy = proxy;
+ }
+
+ public void registerDeathRecorder(DeathRecorder dr) {
+ mDeathRecorder = dr;
+ }
+
+ @Override
+ public void binderDied() {
+ if (mDeathRecorder != null) {
+ mDeathRecorder.deathRecorded = true;
+ }
+ }
+ }
+
+ static class DeathRecorder {
+ public boolean deathRecorded = false;
+ }
+
+ // Try calling System.gc() until an orphaned object is confirmed to be finalized
+ private static void forceGc() {
+ Object obj = new Object();
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+ obj = null; // make it an orphan
+ while (refQueue.poll() != ref) {
+ System.gc();
+ }
+ }
+
+ private static int getSdkVersion() {
+ return ApplicationProvider.getApplicationContext().getApplicationInfo().targetSdkVersion;
+ }
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/MyService.java b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
new file mode 100644
index 000000000000..c701253446f4
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.binder;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+
+public class MyService extends Service {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new IFooProvider.Stub() {
+ ReferenceQueue<IFoo> mRefQueue = new ReferenceQueue<>();
+ PhantomReference<IFoo> mRef;
+
+ @Override
+ public IFoo createFoo() throws RemoteException {
+ IFoo binder = new IFoo.Stub() {};
+ mRef = new PhantomReference<>(binder, mRefQueue);
+ return binder;
+ }
+
+ @Override
+ public boolean isFooGarbageCollected() throws RemoteException {
+ forceGc();
+ return mRefQueue.poll() == mRef;
+ }
+
+ @Override
+ public void killProcess() throws RemoteException {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ };
+ }
+
+ private static void forceGc() {
+ Object obj = new Object();
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+ obj = null; // make it an orphan
+ while (refQueue.poll() != ref) {
+ System.gc();
+ }
+ }
+}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 06cbeb5368a5..330bc84d7a76 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -124,7 +124,7 @@ public class WindowManagerPermissionTests extends TestCase {
@SmallTest
public void testSET_ORIENTATION() {
try {
- mWm.freezeRotation(-1);
+ mWm.freezeRotation(/* rotation= */ -1, /* caller= */ "WindowManagerPermissionTests");
fail("IWindowManager.freezeRotation did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -134,7 +134,7 @@ public class WindowManagerPermissionTests extends TestCase {
}
try {
- mWm.thawRotation();
+ mWm.thawRotation(/* called= */ "WindowManagerPermissionTests");
fail("IWindowManager.thawRotation did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index f7719a6e55b2..95cbff9eeb12 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -38,6 +38,10 @@ public class HostTestUtils {
private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv(
"HOSTTEST_SKIP_METHOD_LOG"));
+ /** If true, we won't print class load log. */
+ private static final boolean SKIP_CLASS_LOG = "1".equals(System.getenv(
+ "HOSTTEST_SKIP_CLASS_LOG"));
+
/** If true, we won't perform non-stub method direct call check. */
private static final boolean SKIP_NON_STUB_METHOD_CHECK = "1".equals(System.getenv(
"HOSTTEST_SKIP_NON_STUB_METHOD_CHECK"));
@@ -57,17 +61,34 @@ public class HostTestUtils {
}
/**
- * Called from methods with FilterPolicy.Log.
+ * Trampoline method for method-call-hook.
+ */
+ public static void callMethodCallHook(
+ Class<?> methodClass,
+ String methodName,
+ String methodDescriptor,
+ String callbackMethod
+ ) {
+ callStaticMethodByName(callbackMethod, methodClass, methodName, methodDescriptor);
+ }
+
+ /**
+ * I can be used as
+ * {@code --default-method-call-hook
+ * com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall}.
+ *
+ * It logs every single methods called.
*/
public static void logMethodCall(
- String methodClass,
+ Class<?> methodClass,
String methodName,
String methodDescriptor
) {
if (SKIP_METHOD_LOG) {
return;
}
- logPrintStream.println("# " + methodClass + "." + methodName + methodDescriptor);
+ logPrintStream.println("# method called: " + methodClass.getCanonicalName() + "."
+ + methodName + methodDescriptor);
}
private static final StackWalker sStackWalker =
@@ -146,15 +167,19 @@ public class HostTestUtils {
logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName()
+ " calling hook " + callbackMethod);
+ callStaticMethodByName(callbackMethod, loadedClass);
+ }
+
+ private static void callStaticMethodByName(String classAndMethodName, Object... args) {
// Forward the call to callbackMethod.
- final int lastPeriod = callbackMethod.lastIndexOf(".");
- final String className = callbackMethod.substring(0, lastPeriod);
- final String methodName = callbackMethod.substring(lastPeriod + 1);
+ final int lastPeriod = classAndMethodName.lastIndexOf(".");
+ final String className = classAndMethodName.substring(0, lastPeriod);
+ final String methodName = classAndMethodName.substring(lastPeriod + 1);
if (lastPeriod < 0 || className.isEmpty() || methodName.isEmpty()) {
throw new HostTestException(String.format(
"Unable to find class load hook: malformed method name \"%s\"",
- callbackMethod));
+ classAndMethodName));
}
Class<?> clazz = null;
@@ -169,13 +194,19 @@ public class HostTestUtils {
"Unable to find class load hook: Class %s must be public", className));
}
+ Class<?>[] argTypes = new Class[args.length];
+ for (int i = 0; i < args.length; i++) {
+ argTypes[i] = args[i].getClass();
+ }
+
Method method = null;
try {
- method = clazz.getMethod(methodName, Class.class);
+ method = clazz.getMethod(methodName, argTypes);
} catch (Exception e) {
throw new HostTestException(String.format(
"Unable to find class load hook: class %s doesn't have method %s"
- + " (method must take exactly one parameter of type Class, and public static)",
+ + " (method must take exactly one parameter of type Class,"
+ + " and public static)",
className,
methodName), e);
}
@@ -186,7 +217,7 @@ public class HostTestUtils {
methodName, className));
}
try {
- method.invoke(null, loadedClass);
+ method.invoke(null, args);
} catch (Exception e) {
throw new HostTestException(String.format(
"Unable to invoke class load hook %s.%s",
@@ -194,4 +225,18 @@ public class HostTestUtils {
methodName), e);
}
}
+
+ /**
+ * I can be used as
+ * {@code --default-class-load-hook
+ * com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded}.
+ *
+ * It logs every loaded class.
+ */
+ public static void logClassLoaded(Class<?> clazz) {
+ if (SKIP_CLASS_LOG) {
+ return;
+ }
+ logPrintStream.println("# class loaded: " + clazz.getCanonicalName());
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index 828d2a3e01c6..3f875272d0a8 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -6,8 +6,10 @@
--enable-non-stub-method-check
# --no-non-stub-method-check
-# --enable-method-logging
-
+#--default-method-call-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+#--default-class-load-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 8db4b6961376..7531759aa106 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -17,6 +17,7 @@ package com.android.hoststubgen
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.filters.AnnotationBasedFilter
+import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
import com.android.hoststubgen.filters.ConstantFilter
import com.android.hoststubgen.filters.FilterPolicy
@@ -156,22 +157,29 @@ class HostStubGen(val options: HostStubGenOptions) {
// This is used when a member (methods, fields, nested classes) don't get any polices
// from upper filters. e.g. when a method has no annotations, then this filter will apply
// the class-wide policy, if any. (if not, we'll fall back to the above filter.)
- val classWidePropagator = ClassWidePolicyPropagatingFilter(filter)
+ filter = ClassWidePolicyPropagatingFilter(filter)
+
+ // Inject default hooks from options.
+ filter = DefaultHookInjectingFilter(
+ options.defaultClassLoadHook,
+ options.defaultMethodCallHook,
+ filter
+ )
// Next, Java annotation based filter.
filter = AnnotationBasedFilter(
- errors,
- allClasses,
- options.stubAnnotations,
- options.keepAnnotations,
- options.stubClassAnnotations,
- options.keepClassAnnotations,
- options.throwAnnotations,
- options.removeAnnotations,
- options.substituteAnnotations,
- options.nativeSubstituteAnnotations,
- options.classLoadHookAnnotations,
- classWidePropagator
+ errors,
+ allClasses,
+ options.stubAnnotations,
+ options.keepAnnotations,
+ options.stubClassAnnotations,
+ options.keepClassAnnotations,
+ options.throwAnnotations,
+ options.removeAnnotations,
+ options.substituteAnnotations,
+ options.nativeSubstituteAnnotations,
+ options.classLoadHookAnnotations,
+ filter
)
// Next, "text based" filter, which allows to override polices without touching
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 9a54ecffc8c2..bbb7dab2c910 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -48,6 +48,9 @@ class HostStubGenOptions(
var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
+ var defaultClassLoadHook: String? = null,
+ var defaultMethodCallHook: String? = null,
+
var intersectStubJars: MutableSet<String> = mutableSetOf(),
var policyOverrideFile: String? = null,
@@ -63,8 +66,6 @@ class HostStubGenOptions(
var enablePreTrace: Boolean = false,
var enablePostTrace: Boolean = false,
- var enableMethodLogging: Boolean = false,
-
var enableNonStubMethodCallDetection: Boolean = true,
) {
companion object {
@@ -151,6 +152,12 @@ class HostStubGenOptions(
ret.classLoadHookAnnotations +=
ensureUniqueAnnotation(ai.nextArgRequired(arg))
+ "--default-class-load-hook" ->
+ ret.defaultClassLoadHook = ai.nextArgRequired(arg)
+
+ "--default-method-call-hook" ->
+ ret.defaultMethodCallHook = ai.nextArgRequired(arg)
+
"--intersect-stub-jar" ->
ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists()
@@ -167,9 +174,6 @@ class HostStubGenOptions(
"--enable-post-trace" -> ret.enablePostTrace = true
"--no-post-trace" -> ret.enablePostTrace = false
- "--enable-method-logging" -> ret.enableMethodLogging = true
- "--no-method-logging" -> ret.enableMethodLogging = false
-
"--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true
"--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false
@@ -290,6 +294,8 @@ class HostStubGenOptions(
substituteAnnotations=$substituteAnnotations,
nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
classLoadHookAnnotations=$classLoadHookAnnotations,
+ defaultClassLoadHook=$defaultClassLoadHook,
+ defaultMethodCallHook=$defaultMethodCallHook,
intersectStubJars=$intersectStubJars,
policyOverrideFile=$policyOverrideFile,
defaultPolicy=$defaultPolicy,
@@ -299,7 +305,6 @@ class HostStubGenOptions(
enableClassChecker=$enableClassChecker,
enablePreTrace=$enablePreTrace,
enablePostTrace=$enablePostTrace,
- enableMethodLogging=$enableMethodLogging,
enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
}
""".trimIndent()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
index 9fbd6d09bfb0..7d7852ab162e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -31,3 +31,23 @@ fun normalizeTextLine(s: String): String {
// Remove surrounding whitespace.
return uncommented.trim()
}
+
+fun <T> addLists(a: List<T>, b: List<T>): List<T> {
+ if (a.isEmpty()) {
+ return b
+ }
+ if (b.isEmpty()) {
+ return a
+ }
+ return a + b
+}
+
+fun <T> addNonNullElement(a: List<T>, b: T?): List<T> {
+ if (b == null) {
+ return a
+ }
+ if (a.isEmpty()) {
+ return listOf(b)
+ }
+ return a + b
+} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 454569d2f1c5..3f492e834637 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -19,6 +19,7 @@ import com.android.hoststubgen.ClassParseException
import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.HostStubGenInternalException
import com.android.hoststubgen.InvalidAnnotationException
+import com.android.hoststubgen.addNonNullElement
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.findAnnotationValueAsString
import com.android.hoststubgen.asm.findAnyAnnotation
@@ -253,14 +254,14 @@ class AnnotationBasedFilter(
return null
}
- override fun getClassLoadHook(className: String): String? {
- classes.getClass(className).let { cn ->
+ override fun getClassLoadHooks(className: String): List<String> {
+ val e = classes.getClass(className).let { cn ->
findAnyAnnotation(classLoadHookAnnotations,
cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
- return getAnnotationField(an, "value")?.toHumanReadableMethodName()
+ getAnnotationField(an, "value")?.toHumanReadableMethodName()
}
}
- return null
+ return addNonNullElement(super.getClassLoadHooks(className), e)
}
private data class MethodKey(val name: String, val desc: String)
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
new file mode 100644
index 000000000000..d771003a955d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.filters
+
+import com.android.hoststubgen.addLists
+
+class DefaultHookInjectingFilter(
+ defaultClassLoadHook: String?,
+ defaultMethodCallHook: String?,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ /**
+ * Create a List containing a single element [e], if e != null. Otherwise, returns
+ * an empty list.
+ */
+ private fun toSingleList(e: String?): List<String> {
+ if (e == null) {
+ return emptyList()
+ }
+ return listOf(e)
+ }
+
+ private val defaultClassLoadHookAsList: List<String> = toSingleList(defaultClassLoadHook)
+ private val defaultMethodCallHookAsList: List<String> = toSingleList(defaultMethodCallHook)
+
+ override fun getClassLoadHooks(className: String): List<String> {
+ return addLists(super.getClassLoadHooks(className), defaultClassLoadHookAsList)
+ }
+
+ override fun getMethodCallHooks(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): List<String> {
+ return addLists(
+ super.getMethodCallHooks(className, methodName, descriptor),
+ defaultMethodCallHookAsList,
+ )
+ }
+} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
index f0763c4ba097..45f61c5b2c22 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -66,7 +66,15 @@ abstract class DelegatingFilter(
return fallback.getNativeSubstitutionClass(className)
}
- override fun getClassLoadHook(className: String): String? {
- return fallback.getClassLoadHook(className)
+ override fun getClassLoadHooks(className: String): List<String> {
+ return fallback.getClassLoadHooks(className)
+ }
+
+ override fun getMethodCallHooks(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): List<String> {
+ return fallback.getMethodCallHooks(className, methodName, descriptor)
}
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index f3551d49bd36..5659a35170c1 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -16,6 +16,7 @@
package com.android.hoststubgen.filters
import com.android.hoststubgen.UnknownApiException
+import com.android.hoststubgen.addNonNullElement
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toHumanReadableMethodName
@@ -127,9 +128,9 @@ class InMemoryOutputFilter(
mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName()
}
- override fun getClassLoadHook(className: String): String? {
- return mClassLoadHooks[getClassKey(className)]
- ?: super.getClassLoadHook(className)
+ override fun getClassLoadHooks(className: String): List<String> {
+ return addNonNullElement(super.getClassLoadHooks(className),
+ mClassLoadHooks[getClassKey(className)])
}
fun setClassLoadHook(className: String, methodName: String) {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
index 392ee4b81613..3df16ffa99b3 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
@@ -71,11 +71,22 @@ abstract class OutputFilter {
}
/**
- * Return a "class load hook" method name for a given class.
+ * Return a "class load hook" class name for a given class.
*
* (which corresponds to @HostSideTestClassLoadHook of the standard annotations.)
*/
- open fun getClassLoadHook(className: String): String? {
- return null
+ open fun getClassLoadHooks(className: String): List<String> {
+ return emptyList()
+ }
+
+ /**
+ * Return the "method call hook" class name.
+ *
+ * The class has to have a function with the following signature:
+ * `public static void onMethodCalled(Class<?> clazz, String name, String descriptor)`.
+ */
+ open fun getMethodCallHooks(className: String, methodName: String, descriptor: String):
+ List<String> {
+ return emptyList()
}
} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index ac068861ec8d..57b668954f98 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -45,7 +45,7 @@ class ImplGeneratingAdapter(
return policy.needsInImpl
}
- private var classLoadHookMethod: String? = null
+ private var classLoadHooks: List<String> = emptyList()
override fun visit(
version: Int,
@@ -57,22 +57,22 @@ class ImplGeneratingAdapter(
) {
super.visit(version, access, name, signature, superName, interfaces)
- classLoadHookMethod = filter.getClassLoadHook(currentClassName)
+ classLoadHooks = filter.getClassLoadHooks(currentClassName)
// classLoadHookMethod is non-null, then we need to inject code to call it
// in the class initializer.
// If the target class already has a class initializer, then we need to inject code to it.
// Otherwise, we need to create one.
- classLoadHookMethod?.let { callback ->
- log.d(" ClassLoadHook: $callback")
+ if (classLoadHooks.isNotEmpty()) {
+ log.d(" ClassLoadHooks: $classLoadHooks")
if (!classes.hasClassInitializer(currentClassName)) {
- injectClassLoadHook(callback)
+ injectClassLoadHook()
}
}
}
- private fun injectClassLoadHook(callback: String) {
+ private fun injectClassLoadHook() {
writeRawMembers {
// Create a class initializer to call onClassLoaded().
// Each class can only have at most one class initializer, but the base class
@@ -87,7 +87,7 @@ class ImplGeneratingAdapter(
// Method prologue
mv.visitCode()
- writeClassLoadHookCall(mv)
+ writeClassLoadHookCalls(mv)
mv.visitInsn(Opcodes.RETURN)
// Method epilogue
@@ -97,21 +97,23 @@ class ImplGeneratingAdapter(
}
}
- private fun writeClassLoadHookCall(mv: MethodVisitor) {
- // First argument: the class type.
- mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
-
- // Second argument: method name
- mv.visitLdcInsn(classLoadHookMethod)
-
- // Call HostTestUtils.onClassLoaded().
- mv.visitMethodInsn(
- Opcodes.INVOKESTATIC,
- HostTestUtils.CLASS_INTERNAL_NAME,
- "onClassLoaded",
- "(Ljava/lang/Class;Ljava/lang/String;)V",
- false
- )
+ private fun writeClassLoadHookCalls(mv: MethodVisitor) {
+ classLoadHooks.forEach { classLoadHook ->
+ // First argument: the class type.
+ mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+
+ // Second argument: method name
+ mv.visitLdcInsn(classLoadHook)
+
+ // Call HostTestUtils.onClassLoaded().
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "onClassLoaded",
+ "(Ljava/lang/Class;Ljava/lang/String;)V",
+ false
+ )
+ }
}
override fun updateAccessFlags(
@@ -138,20 +140,22 @@ class ImplGeneratingAdapter(
var innerVisitor = superVisitor
// If method logging is enabled, inject call to the logging method.
- if (options.enableMethodLogging) {
- innerVisitor = LogInjectingMethodAdapter(
- access,
- name,
- descriptor,
- signature,
- exceptions,
- innerVisitor,
- )
+ val methodCallHooks = filter.getMethodCallHooks(currentClassName, name, descriptor)
+ if (methodCallHooks.isNotEmpty()) {
+ innerVisitor = MethodCallHookInjectingAdapter(
+ access,
+ name,
+ descriptor,
+ signature,
+ exceptions,
+ innerVisitor,
+ methodCallHooks,
+ )
}
// If this class already has a class initializer and a class load hook is needed, then
// we inject code.
- if (classLoadHookMethod != null &&
+ if (classLoadHooks.isNotEmpty() &&
name == CLASS_INITIALIZER_NAME &&
descriptor == CLASS_INITIALIZER_DESC) {
innerVisitor = ClassLoadHookInjectingMethodAdapter(
@@ -283,29 +287,37 @@ class ImplGeneratingAdapter(
}
/**
- * A method adapter that injects a call to HostTestUtils.logMethodCall() to every method.
+ * Inject calls to the method call hooks.
*
* Note, when the target method is a constructor, it may contain calls to `super(...)` or
* `this(...)`. The logging code will be injected *before* such calls.
*/
- private inner class LogInjectingMethodAdapter(
+ private inner class MethodCallHookInjectingAdapter(
access: Int,
val name: String,
val descriptor: String,
signature: String?,
exceptions: Array<String>?,
- next: MethodVisitor?
+ next: MethodVisitor?,
+ val hooks: List<String>,
) : MethodVisitor(OPCODE_VERSION, next) {
override fun visitCode() {
super.visitCode()
- visitLdcInsn(currentClassName)
- visitLdcInsn(name)
- visitLdcInsn(descriptor)
- visitMethodInsn(Opcodes.INVOKESTATIC,
+
+ hooks.forEach { hook ->
+ mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+ visitLdcInsn(name)
+ visitLdcInsn(descriptor)
+ visitLdcInsn(hook)
+
+ visitMethodInsn(
+ Opcodes.INVOKESTATIC,
HostTestUtils.CLASS_INTERNAL_NAME,
- "logMethodCall",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
- false)
+ "callMethodCallHook",
+ "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ false
+ )
+ }
}
}
@@ -323,7 +335,7 @@ class ImplGeneratingAdapter(
override fun visitCode() {
super.visitCode()
- writeClassLoadHookCall(this)
+ writeClassLoadHookCalls(this)
}
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
index 8c76a612020f..05d6a43cdb0f 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -53,6 +53,46 @@ java_genrule_host {
],
}
+// Same as "hoststubgen-test-tiny-framework-host", but with more options, to test more hoststubgen
+// features.
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext",
+ defaults: ["hoststubgen-command-defaults"],
+ cmd: hoststubgen_common_options +
+ "--in-jar $(location :hoststubgen-test-tiny-framework) " +
+ "--policy-override-file $(location policy-override-tiny-framework.txt) " +
+
+ // More options.
+ "--default-method-call-hook com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall " +
+ "--default-class-load-hook com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded ",
+ srcs: [
+ ":hoststubgen-test-tiny-framework",
+ "policy-override-tiny-framework.txt",
+ ],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-stub",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext{host_stub.jar}",
+ ],
+ out: [
+ "host_stub.jar",
+ ],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-impl",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext{host_impl.jar}",
+ ],
+ out: [
+ "host_impl.jar",
+ ],
+}
+
// Compile the test jar, using 2 rules.
// 1. Build the test against the stub.
java_library_host {
@@ -123,6 +163,30 @@ java_genrule_host {
visibility: ["//visibility:private"],
}
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-stub-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext-stub",
+ ],
+ out: [
+ "12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-ext-impl-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-ext-impl",
+ ],
+ out: [
+ "13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
// Run it with `atest`. Compare the dump of the jar files to the golden output.
python_test_host {
name: "tiny-framework-dump-test",
@@ -136,6 +200,8 @@ python_test_host {
"hoststubgen-test-tiny-framework-host-stub-dump",
"hoststubgen-test-tiny-framework-host-impl-dump",
"hoststubgen-test-tiny-framework-orig-dump",
+ "hoststubgen-test-tiny-framework-host-ext-stub-dump",
+ "hoststubgen-test-tiny-framework-host-ext-impl-dump",
],
test_suites: ["general-tests"],
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 4d588694b8da..639fb16c4251 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -112,7 +112,7 @@ done
if (( $three_way )) ; then
echo "# Running 3-way diff with meld..."
- run meld ${files[*]} &
+ run meld ${files[0]} ${files[1]} ${files[2]} &
fi
if (( $two_way )) ; then
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 1aa485963f97..0c1d88ad0ff9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -13,11 +13,11 @@ public interface android.hosttest.annotation.HostSideTestClassLoadHook extends j
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -33,11 +33,11 @@ public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -56,11 +56,11 @@ public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass
}
SourceFile: "HostSideTestNativeSubstitutionClass.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -76,11 +76,11 @@ public interface android.hosttest.annotation.HostSideTestRemove extends java.lan
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -96,11 +96,11 @@ public interface android.hosttest.annotation.HostSideTestStub extends java.lang.
}
SourceFile: "HostSideTestStub.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -119,11 +119,11 @@ public interface android.hosttest.annotation.HostSideTestSubstitute extends java
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -139,11 +139,11 @@ public interface android.hosttest.annotation.HostSideTestThrow extends java.lang
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -159,11 +159,11 @@ public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -179,11 +179,11 @@ public interface android.hosttest.annotation.HostSideTestWholeClassStub extends
}
SourceFile: "HostSideTestWholeClassStub.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -199,7 +199,7 @@ public interface android.hosttest.annotation.tests.HostSideTestSuppress extends
}
SourceFile: "HostSideTestSuppress.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
)
@@ -217,9 +217,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -230,11 +230,11 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public static int getOneStub();
@@ -242,11 +242,11 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkCallerCheck.java"
@@ -267,9 +267,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -280,8 +280,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
LineNumberTable:
public static int getOne_noCheck();
@@ -289,13 +289,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
LineNumberTable:
}
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -314,14 +314,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public int remove;
@@ -333,21 +333,21 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -355,17 +355,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 6 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOneInner(int);
@@ -373,17 +373,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 4 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public void toBeRemoved(java.lang.String);
@@ -391,17 +391,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 8 1 foo Ljava/lang/String;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestRemove
public int addTwo(int);
@@ -409,20 +409,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -432,10 +432,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -446,9 +446,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -458,10 +458,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -472,14 +472,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestThrow
public java.lang.String visibleButUsesUnsupportedMethod();
@@ -487,22 +487,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -532,15 +532,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -551,10 +551,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -566,10 +566,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -581,10 +581,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -596,20 +596,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -619,10 +619,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -633,9 +633,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -645,10 +645,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -659,8 +659,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -671,9 +671,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -681,7 +681,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -702,9 +702,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -715,11 +715,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 3: aload_0
- 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
- 9: pop
- 10: return
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -734,16 +734,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class java/util/HashSet
- 3: dup
- 4: invokespecial #x // Method java/util/HashSet."<init>":()V
- 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 10: return
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -763,9 +763,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -776,18 +776,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: putstatic #x // Field sInitialized:Z
- 4: return
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -803,9 +803,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -816,18 +816,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
- 0: new #x // class java/lang/IllegalStateException
- 3: dup
- 4: ldc #x // String Inner exception
- 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
- 9: athrow
- 10: astore_0
- 11: new #x // class java/lang/RuntimeException
- 14: dup
- 15: ldc #x // String Outer exception
- 17: aload_0
- 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
- 21: athrow
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
Exception table:
from to target type
0 10 10 Class java/lang/Exception
@@ -841,7 +841,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -869,15 +869,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -888,10 +888,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -903,10 +903,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -918,10 +918,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -933,11 +933,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -949,10 +949,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -968,10 +968,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -982,8 +982,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -994,9 +994,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1017,9 +1017,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1034,9 +1034,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1051,10 +1051,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1063,9 +1063,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -1083,9 +1083,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1096,10 +1096,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1110,10 +1110,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: ladd
- 3: lreturn
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1122,7 +1122,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -1142,12 +1142,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1159,9 +1159,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_1
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1172,9 +1172,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1200,9 +1200,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1213,9 +1213,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_2
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1226,9 +1226,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1258,12 +1258,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1275,9 +1275,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_3
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1288,9 +1288,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1316,9 +1316,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1329,9 +1329,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_4
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1342,9 +1342,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1374,12 +1374,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iload_1
- 6: putfield #x // Field value:I
- 9: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1412,15 +1412,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: aload_0
- 10: iconst_5
- 11: putfield #x // Field value:I
- 14: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1429,7 +1429,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
@@ -1448,9 +1448,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1461,9 +1461,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: bipush 7
- 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 5: areturn
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1474,9 +1474,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1507,12 +1507,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: bipush 6
- 7: putfield #x // Field value:I
- 10: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1523,16 +1523,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
@@ -1552,10 +1552,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
- 5: return
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1591,15 +1591,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 8: dup
- 9: aload_0
- 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
- 16: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1610,11 +1610,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 3: dup
- 4: aload_0
- 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 8: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1626,10 +1626,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
@@ -1638,16 +1638,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
- 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
- 10: return
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 6e1528aedc1e..43ceec42660d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -12,33 +12,33 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOneStub();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -55,44 +55,44 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOne_withCheck();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOne_noCheck();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -109,7 +109,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
@@ -117,13 +117,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -131,13 +131,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addTwo(int);
@@ -145,47 +145,47 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -215,97 +215,97 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOneInner(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public void toBeRemoved(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addTwo(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -326,22 +326,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static void onClassLoaded(java.lang.Class<?>);
descriptor: (Ljava/lang/Class;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // (Ljava/lang/Class<*>;)V
static {};
@@ -349,20 +349,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -382,35 +382,35 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -426,31 +426,31 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int testException();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -470,61 +470,61 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addTwo(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
@@ -540,11 +540,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static native int nativeAddTwo(int);
descriptor: (I)I
@@ -555,11 +555,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static native long nativeLongPlus(long, long);
descriptor: (JJ)J
@@ -570,22 +570,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -607,19 +607,19 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -644,22 +644,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -680,22 +680,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
InnerClasses:
@@ -703,12 +703,12 @@ InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -725,20 +725,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -765,22 +765,22 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
@@ -788,11 +788,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
static {};
@@ -800,11 +800,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
@@ -818,12 +818,12 @@ InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 5672e9c36706..faf0a46c8fab 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -13,13 +13,13 @@ public interface android.hosttest.annotation.HostSideTestClassLoadHook extends j
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -35,13 +35,13 @@ public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -60,13 +60,13 @@ public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass
}
SourceFile: "HostSideTestNativeSubstitutionClass.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -82,13 +82,13 @@ public interface android.hosttest.annotation.HostSideTestRemove extends java.lan
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -104,13 +104,13 @@ public interface android.hosttest.annotation.HostSideTestStub extends java.lang.
}
SourceFile: "HostSideTestStub.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -129,13 +129,13 @@ public interface android.hosttest.annotation.HostSideTestSubstitute extends java
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -151,13 +151,13 @@ public interface android.hosttest.annotation.HostSideTestThrow extends java.lang
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -173,13 +173,13 @@ public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -195,13 +195,13 @@ public interface android.hosttest.annotation.HostSideTestWholeClassStub extends
}
SourceFile: "HostSideTestWholeClassStub.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -219,9 +219,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -232,17 +232,17 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=0, args_size=0
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- 2: ldc #x // String getOneKeep
- 4: ldc #x // String ()I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_1
- 16: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public static int getOneStub();
@@ -250,20 +250,20 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -280,9 +280,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -293,8 +293,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
LineNumberTable:
public static int getOne_noCheck();
@@ -302,20 +302,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
LineNumberTable:
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -332,14 +332,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
private static {};
@@ -347,31 +347,31 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -379,17 +379,17 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 6 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOneInner(int);
@@ -397,23 +397,23 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String addOneInner
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_1
- 16: iconst_1
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
15 4 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public int addTwo(int);
@@ -421,10 +421,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -436,10 +436,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -450,20 +450,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String unsupportedMethod
- 4: ldc #x // String ()Ljava/lang/String;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- 18: new #x // class java/lang/RuntimeException
- 21: dup
- 22: ldc #x // String Unreachable
- 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 27: athrow
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestThrow
public java.lang.String visibleButUsesUnsupportedMethod();
@@ -471,27 +471,27 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -521,15 +521,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -540,10 +540,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -555,10 +555,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -570,10 +570,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -585,10 +585,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -600,10 +600,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -614,8 +614,8 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -626,9 +626,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -636,12 +636,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -662,9 +662,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -675,11 +675,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 3: aload_0
- 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
- 9: pop
- 10: return
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -694,21 +694,21 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class java/util/HashSet
- 3: dup
- 4: invokespecial #x // Method java/util/HashSet."<init>":()V
- 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 10: return
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -728,9 +728,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -741,26 +741,26 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithIn
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: iconst_1
- 8: putstatic #x // Field sInitialized:Z
- 11: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -776,9 +776,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -789,18 +789,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
- 0: new #x // class java/lang/IllegalStateException
- 3: dup
- 4: ldc #x // String Inner exception
- 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
- 9: athrow
- 10: astore_0
- 11: new #x // class java/lang/RuntimeException
- 14: dup
- 15: ldc #x // String Outer exception
- 17: aload_0
- 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
- 21: athrow
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
Exception table:
from to target type
0 10 10 Class java/lang/Exception
@@ -814,12 +814,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTe
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -843,25 +843,25 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -872,10 +872,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -887,16 +887,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String addOneInner
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_1
- 16: iconst_1
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -908,10 +908,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -923,10 +923,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -937,27 +937,27 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String unsupportedMethod
- 4: ldc #x // String ()Ljava/lang/String;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- 18: new #x // class java/lang/RuntimeException
- 21: dup
- 22: ldc #x // String Unreachable
- 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 27: athrow
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -965,9 +965,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPoli
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
@@ -983,9 +983,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -996,18 +996,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
public static int nativeAddTwo_should_be_like_this(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1018,20 +1018,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
public static long nativeLongPlus_should_be_like_this(long, long);
descriptor: (JJ)J
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1040,14 +1040,14 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -1065,15 +1065,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String <init>
- 4: ldc #x // String ()V
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokespecial #x // Method java/lang/Object."<init>":()V
- 19: return
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1084,16 +1084,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String nativeAddTwo
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_0
- 16: iconst_2
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1104,16 +1104,16 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String nativeLongPlus
- 4: ldc #x // String (JJ)J
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: lload_0
- 16: lload_2
- 17: ladd
- 18: lreturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1122,10 +1122,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -1145,12 +1145,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1162,15 +1162,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_1
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1181,15 +1181,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1201,7 +1201,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframe
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
@@ -1218,9 +1218,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1231,15 +1231,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_2
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1250,15 +1250,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1270,7 +1270,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframe
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
@@ -1291,12 +1291,12 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1308,15 +1308,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_3
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1327,15 +1327,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1347,7 +1347,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframew
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
@@ -1364,9 +1364,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1377,15 +1377,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_4
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1396,15 +1396,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 ex
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1416,7 +1416,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframew
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
@@ -1437,12 +1437,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iload_1
- 6: putfield #x // Field value:I
- 9: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1453,9 +1453,9 @@ InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -1480,15 +1480,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: aload_0
- 10: iconst_5
- 11: putfield #x // Field value:I
- 14: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1499,12 +1499,12 @@ InnerClasses:
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
@@ -1521,9 +1521,9 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1534,15 +1534,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: bipush 7
- 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 20: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1553,15 +1553,15 @@ class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$Stat
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1574,7 +1574,7 @@ EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframew
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -1595,12 +1595,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: bipush 6
- 7: putfield #x // Field value:I
- 10: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1611,10 +1611,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
@@ -1623,12 +1623,12 @@ InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -1645,10 +1645,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
- 5: return
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1660,9 +1660,9 @@ InnerClasses:
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -1689,15 +1689,15 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 8: dup
- 9: aload_0
- 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
- 16: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1708,11 +1708,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 3: dup
- 4: aload_0
- 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 8: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1724,10 +1724,10 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
@@ -1736,11 +1736,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
- 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
- 10: return
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
}
InnerClasses:
@@ -1755,12 +1755,12 @@ InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
new file mode 100644
index 000000000000..43ceec42660d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -0,0 +1,837 @@
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 5
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static native int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static native long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=4, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 1, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
new file mode 100644
index 000000000000..874789e23607
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -0,0 +1,2348 @@
+## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
+ Compiled from "HostSideTestClassLoadHook.java"
+public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 2, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestClassLoadHook
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestKeep.class
+ Compiled from "HostSideTestKeep.java"
+public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestKeep
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestKeep.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
+ Compiled from "HostSideTestNativeSubstitutionClass.java"
+public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 2, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestNativeSubstitutionClass.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRemove.class
+ Compiled from "HostSideTestRemove.java"
+public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRemove
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestRemove
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestRemove.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestStub.class
+ Compiled from "HostSideTestStub.java"
+public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestStub
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestStub.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestSubstitute.class
+ Compiled from "HostSideTestSubstitute.java"
+public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestSubstitute
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 2, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestSubstitute
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public abstract java.lang.String suffix();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestSubstitute.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestThrow.class
+ Compiled from "HostSideTestThrow.java"
+public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestThrow
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestThrow
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestThrow.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
+ Compiled from "HostSideTestWholeClassKeep.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestWholeClassKeep
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestWholeClassKeep.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
+ Compiled from "HostSideTestWholeClassStub.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class android/hosttest/annotation/HostSideTestWholeClassStub
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "HostSideTestWholeClassStub.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ x: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ x: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 4, attributes: 4
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
+
+ public static int getOneKeep();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneStub
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_1
+ x: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 4, attributes: 5
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String getOne_withCheck
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
+ LineNumberTable:
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ x: ldc #x // String getOne_noCheck
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
+ LineNumberTable:
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 11 6 1 value I
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 26 4 1 value I
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 11 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String nativeAddThree
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestThrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String visibleButUsesUnsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 9, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 4 1 value I
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String toBeRemoved
+ x: ldc #x // String (Ljava/lang/String;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 8 1 foo Ljava/lang/String;
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 11 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String nativeAddThree
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ x: ldc #x // String visibleButUsesUnsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String onClassLoaded
+ x: ldc #x // String (Ljava/lang/Class;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 clazz Ljava/lang/Class;
+ LocalVariableTypeTable:
+ Start Length Slot Name Signature
+ 11 11 0 clazz Ljava/lang/Class<*>;
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ x: ldc #x // String testException
+ x: ldc #x // String ()I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
+ Exception table:
+ from to target type
+ 11 21 21 Class java/lang/Exception
+ StackMapTable: number_of_entries = 1
+ frame_type = 85 /* same_locals_1_stack_item */
+ stack = [ class java/lang/Exception ]
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 22 11 0 e Ljava/lang/Exception;
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOne
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 11 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 26 4 1 value I
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 11 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String nativeAddThree
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String visibleButUsesUnsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 6, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeAddTwo_should_be_like_this
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ x: ldc #x // String nativeLongPlus_should_be_like_this
+ x: ldc #x // String (JJ)J
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 arg1 J
+ 11 6 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ x: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
+ Compiled from "TinyFrameworkNative_host.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 4 0 arg1 J
+ 26 4 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative_host.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 4, attributes: 6
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String <init>
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 4, attributes: 6
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 4, attributes: 6
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String <init>
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 4, attributes: 6
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
+ 11 10 1 x I
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 2, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ x: ldc #x // String <init>
+ x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
+ 11 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+}
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 4, attributes: 6
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 26 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
+ 11 6 1 x I
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String getSupplier
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String getSupplier_static
+ x: ldc #x // String ()Ljava/util/function/Supplier;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
+ LineNumberTable:
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
index 93729fb22caa..992665ed58ee 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -93,6 +93,7 @@ filter_output() {
if (( $simple )) ; then
# For "simple output" mode,
# - Normalize the constant numbers (replace with "#x")
+ # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:")
# - Remove the constant pool
# - Remove the line number table
# - Some other transient lines
@@ -100,6 +101,7 @@ filter_output() {
# `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
# the start and the end lines.
sed -e 's/#[0-9][0-9]*/#x/g' \
+ -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
-e '/^Constant pool:/,/^[^ ]/{//!d}' \
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
-e '/SHA-256 checksum/d' \
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 6bc0ddb1a8dc..7600942c99e6 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -34,6 +34,7 @@ run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh
run ./hoststubgen/test-framework/run-test-without-atest.sh
run ./hoststubgen/test-tiny-framework/run-test-manually.sh
+run atest tiny-framework-dump-test
run ./scripts/build-framework-hostside-jars-and-extract.sh
# This script is already broken on goog/master
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index 4a2e37e51d71..2e82beb24b56 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -14,6 +14,7 @@
import argparse
import json
+import functools
import os
import shutil
import subprocess
@@ -28,6 +29,7 @@ SOONG_UI = "build/soong/soong_ui.bash"
PATH_PREFIX = "out/soong/.intermediates"
PATH_SUFFIX = "android_common/lint"
FIX_ZIP = "suggested-fixes.zip"
+MODULE_JAVA_DEPS = "out/soong/module_bp_java_deps.json"
class SoongModule:
@@ -49,11 +51,26 @@ class SoongModule:
print(f"Found module {partial_path}/{self._name}.")
self._path = f"{PATH_PREFIX}/{partial_path}/{self._name}/{PATH_SUFFIX}"
+ def find_java_deps(self, module_java_deps):
+ """Finds the dependencies of a Java module in the loaded module_bp_java_deps.json.
+
+ Returns:
+ A list of module names.
+ """
+ if self._name not in module_java_deps:
+ raise Exception(f"Module {self._name} not found!")
+
+ return module_java_deps[self._name]["dependencies"]
+
@property
def name(self):
return self._name
@property
+ def path(self):
+ return self._path
+
+ @property
def lint_report(self):
return f"{self._path}/lint-report.txt"
@@ -62,52 +79,25 @@ class SoongModule:
return f"{self._path}/{FIX_ZIP}"
-class SoongLintFix:
+class SoongLintWrapper:
"""
- This class creates a command line tool that will apply lint fixes to the
- platform via the necessary combination of soong and shell commands.
+ This class wraps the necessary calls to Soong and/or shell commands to lint
+ platform modules and apply suggested fixes if desired.
- It breaks up these operations into a few "private" methods that are
- intentionally exposed so experimental code can tweak behavior.
-
- The entry point, `run`, will apply lint fixes using the intermediate
- `suggested-fixes` directory that soong creates during its invocation of
- lint.
-
- Basic usage:
- ```
- from soong_lint_fix import SoongLintFix
-
- opts = SoongLintFixOptions()
- opts.parse_args(sys.argv)
- SoongLintFix(opts).run()
- ```
+ It breaks up these operations into a few methods that are available to
+ sub-classes (see SoongLintFix for an example).
"""
- def __init__(self, opts):
- self._opts = opts
+ def __init__(self, check=None, lint_module=None):
+ self._check = check
+ self._lint_module = lint_module
self._kwargs = None
- self._modules = []
-
- def run(self):
- """
- Run the script
- """
- self._setup()
- self._find_modules()
- self._lint()
-
- if not self._opts.no_fix:
- self._fix()
-
- if self._opts.print:
- self._print()
def _setup(self):
env = os.environ.copy()
- if self._opts.check:
- env["ANDROID_LINT_CHECK"] = self._opts.check
- if self._opts.lint_module:
- env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module
+ if self._check:
+ env["ANDROID_LINT_CHECK"] = self._check
+ if self._lint_module:
+ env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._lint_module
self._kwargs = {
"env": env,
@@ -117,7 +107,10 @@ class SoongLintFix:
os.chdir(ANDROID_BUILD_TOP)
- print("Refreshing soong modules...")
+ @functools.cached_property
+ def _module_info(self):
+ """Returns the JSON content of module-info.json."""
+ print("Refreshing Soong modules...")
try:
os.mkdir(ANDROID_PRODUCT_OUT)
except OSError:
@@ -125,19 +118,54 @@ class SoongLintFix:
subprocess.call(f"{SOONG_UI} --make-mode {PRODUCT_OUT}/module-info.json", **self._kwargs)
print("done.")
-
- def _find_modules(self):
with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
- module_info = json.load(f)
+ return json.load(f)
- for module_name in self._opts.modules:
- module = SoongModule(module_name)
- module.find(module_info)
- self._modules.append(module)
+ def _find_module(self, module_name):
+ """Returns a SoongModule from a module name.
- def _lint(self):
+ Ensures that the module is known to Soong.
+ """
+ module = SoongModule(module_name)
+ module.find(self._module_info)
+ return module
+
+ def _find_modules(self, module_names):
+ modules = []
+ for module_name in module_names:
+ modules.append(self._find_module(module_name))
+ return modules
+
+ @functools.cached_property
+ def _module_java_deps(self):
+ """Returns the JSON content of module_bp_java_deps.json."""
+ print("Refreshing Soong Java deps...")
+ subprocess.call(f"{SOONG_UI} --make-mode {MODULE_JAVA_DEPS}", **self._kwargs)
+ print("done.")
+
+ with open(f"{MODULE_JAVA_DEPS}") as f:
+ return json.load(f)
+
+ def _find_module_java_deps(self, module):
+ """Returns a list a dependencies for a module.
+
+ Args:
+ module: A SoongModule.
+
+ Returns:
+ A list of SoongModule.
+ """
+ deps = []
+ dep_names = module.find_java_deps(self._module_java_deps)
+ for dep_name in dep_names:
+ dep = SoongModule(dep_name)
+ dep.find(self._module_info)
+ deps.append(dep)
+ return deps
+
+ def _lint(self, modules):
print("Cleaning up any old lint results...")
- for module in self._modules:
+ for module in modules:
try:
os.remove(f"{module.lint_report}")
os.remove(f"{module.suggested_fixes}")
@@ -145,13 +173,13 @@ class SoongLintFix:
pass
print("done.")
- target = " ".join([ module.lint_report for module in self._modules ])
+ target = " ".join([ module.lint_report for module in modules ])
print(f"Generating {target}")
subprocess.call(f"{SOONG_UI} --make-mode {target}", **self._kwargs)
print("done.")
- def _fix(self):
- for module in self._modules:
+ def _fix(self, modules):
+ for module in modules:
print(f"Copying suggested fixes for {module.name} to the tree...")
with zipfile.ZipFile(f"{module.suggested_fixes}") as zip:
for name in zip.namelist():
@@ -161,13 +189,40 @@ class SoongLintFix:
shutil.copyfileobj(src, dst)
print("done.")
- def _print(self):
- for module in self._modules:
+ def _print(self, modules):
+ for module in modules:
print(f"### lint-report.txt {module.name} ###", end="\n\n")
with open(module.lint_report, "r") as f:
print(f.read())
+class SoongLintFix(SoongLintWrapper):
+ """
+ Basic usage:
+ ```
+ from soong_lint_fix import SoongLintFix
+
+ opts = SoongLintFixOptions()
+ opts.parse_args()
+ SoongLintFix(opts).run()
+ ```
+ """
+ def __init__(self, opts):
+ super().__init__(check=opts.check, lint_module=opts.lint_module)
+ self._opts = opts
+
+ def run(self):
+ self._setup()
+ modules = self._find_modules(self._opts.modules)
+ self._lint(modules)
+
+ if not self._opts.no_fix:
+ self._fix(modules)
+
+ if self._opts.print:
+ self._print(modules)
+
+
class SoongLintFixOptions:
"""Options for SoongLintFix"""
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
index cbbf91b49ded..758de4dfccf8 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
@@ -131,7 +131,7 @@ class EnforcePermissionHelperDetector : Detector(), SourceCodeScanner {
priority = 6,
severity = Severity.ERROR,
implementation = Implementation(
- EnforcePermissionDetector::class.java,
+ EnforcePermissionHelperDetector::class.java,
EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py
index b5c2ffe9885e..a4c00f79b63f 100644
--- a/tools/lint/utils/enforce_permission_counter.py
+++ b/tools/lint/utils/enforce_permission_counter.py
@@ -16,57 +16,38 @@ import re
import soong_lint_fix
-# Libraries that constitute system_server.
-# It is non-trivial to keep in sync with services/Android.bp as some
-# module are post-processed (e.g, services.core).
-TARGETS = [
- "services.core.unboosted",
- "services.accessibility",
- "services.appprediction",
- "services.appwidget",
- "services.autofill",
- "services.backup",
- "services.companion",
- "services.contentcapture",
- "services.contentsuggestions",
- "services.coverage",
- "services.devicepolicy",
- "services.midi",
- "services.musicsearch",
- "services.net",
- "services.people",
- "services.print",
- "services.profcollect",
- "services.restrictions",
- "services.searchui",
- "services.smartspace",
- "services.systemcaptions",
- "services.translation",
- "services.texttospeech",
- "services.usage",
- "services.usb",
- "services.voiceinteraction",
- "services.wallpapereffectsgeneration",
- "services.wifi",
-]
+CHECK = "AnnotatedAidlCounter"
+LINT_MODULE = "AndroidUtilsLintChecker"
-
-class EnforcePermissionMigratedCounter:
+class EnforcePermissionMigratedCounter(soong_lint_fix.SoongLintWrapper):
"""Wrapper around lint_fix to count the number of AIDL methods annotated."""
- def run(self):
- opts = soong_lint_fix.SoongLintFixOptions()
- opts.check = "AnnotatedAidlCounter"
- opts.lint_module = "AndroidUtilsLintChecker"
- opts.no_fix = True
- opts.modules = TARGETS
- self.linter = soong_lint_fix.SoongLintFix(opts)
- self.linter.run()
- self.parse_lint_reports()
+ def __init__(self):
+ super().__init__(check=CHECK, lint_module=LINT_MODULE)
+
+ def run(self):
+ self._setup()
+
+ # Analyze the dependencies of the "services" module and the module
+ # "services.core.unboosted".
+ service_module = self._find_module("services")
+ dep_modules = self._find_module_java_deps(service_module) + \
+ [self._find_module("services.core.unboosted")]
+
+ # Skip dependencies that are not services. Skip the "services.core"
+ # module which is analyzed via "services.core.unboosted".
+ modules = []
+ for module in dep_modules:
+ if "frameworks/base/services" not in module.path:
+ continue
+ if module.name == "services.core":
+ continue
+ modules.append(module)
+
+ self._lint(modules)
- def parse_lint_reports(self):
counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 }
- for module in self.linter._modules:
+ for module in modules:
with open(module.lint_report, "r") as f:
content = f.read()
keys = dict(re.findall(r'(\w+)=(\d+)', content))