summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp8
-rw-r--r--core/api/current.txt27
-rw-r--r--core/api/system-current.txt142
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java12
-rw-r--r--core/java/android/app/AppOpsManager.java16
-rw-r--r--core/java/android/app/ApplicationPackageManager.java2
-rw-r--r--core/java/android/app/assist/AssistContent.java25
-rw-r--r--core/java/android/companion/AssociationInfo.java64
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java52
-rw-r--r--core/java/android/companion/DeviceId.aidl19
-rw-r--r--core/java/android/companion/DeviceId.java210
-rw-r--r--core/java/android/companion/ICompanionDeviceManager.aidl5
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java3
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java4
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java42
-rw-r--r--core/java/android/os/Build.java13
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java73
-rw-r--r--core/java/android/os/IHintManager.aidl6
-rw-r--r--core/java/android/os/IHintSession.aidl5
-rw-r--r--core/java/android/os/SessionCreationConfig.aidl8
-rw-r--r--core/java/android/permission/flags.aconfig9
-rw-r--r--core/java/android/provider/ContactsContract.java22
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig7
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java1
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java2
-rw-r--r--core/java/android/service/voice/OWNERS2
-rw-r--r--core/java/android/telephony/TelephonyCallback.java30
-rw-r--r--core/java/android/view/ViewRootImpl.java18
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/view/autofill/AutofillFeatureFlags.java4
-rw-r--r--core/java/android/view/autofill/AutofillManager.java3
-rw-r--r--core/java/android/view/flags/scroll_capture.aconfig2
-rw-r--r--core/java/android/webkit/UserPackage.java11
-rw-r--r--core/java/android/webkit/WebViewFactory.java10
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java58
-rw-r--r--core/java/android/webkit/flags.aconfig8
-rw-r--r--core/java/android/window/DesktopModeFlags.java13
-rw-r--r--core/java/android/window/KeyguardState.java33
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig12
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java8
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java45
-rw-r--r--core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java314
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/config.xml13
-rw-r--r--core/res/res/values/config_telephony.xml6
-rw-r--r--core/res/res/values/public-staging.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt176
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt63
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java21
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/Android.bp21
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt93
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt78
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt76
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt43
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt114
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java5
-rw-r--r--location/api/system-current.txt8
-rw-r--r--location/java/android/location/provider/IPopulationDensityProvider.aidl45
-rw-r--r--location/java/android/location/provider/IS2CellIdsCallback.aidl36
-rw-r--r--location/java/android/location/provider/IS2LevelCallback.aidl34
-rw-r--r--location/java/android/location/provider/PopulationDensityProviderBase.java192
-rw-r--r--media/java/android/media/MediaRoute2Info.java36
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java199
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerDetector.java2
-rw-r--r--native/android/OWNERS2
-rw-r--r--native/android/libandroid.map.txt4
-rw-r--r--native/android/performance_hint.cpp160
-rw-r--r--native/android/surface_control.cpp22
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp3
-rw-r--r--nfc/tests/src/android/nfc/NdefRecordTest.java59
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml3
-rw-r--r--packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt1
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml5
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java15
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java106
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java9
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java86
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt41
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java4
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt75
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt34
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt127
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt87
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java17
-rw-r--r--packages/SystemUI/res/values/strings.xml13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationComponent.kt (renamed from packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationComponent.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationModule.kt (renamed from packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt176
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt8
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java33
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java2
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java2
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java2
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java42
-rw-r--r--ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java2
-rw-r--r--ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java2
-rw-r--r--ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java98
-rwxr-xr-xravenwood/scripts/extract-last-soong-commands.py89
-rw-r--r--ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java108
-rw-r--r--ravenwood/texts/ravenwood-common-policies.txt6
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt9
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt19
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt7
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt4
-rw-r--r--services/accessibility/accessibility.aconfig15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java34
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ProxyManager.java72
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java11
-rw-r--r--services/autofill/bugfixes.aconfig10
-rw-r--r--services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java95
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java3
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java33
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java3
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java3
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java19
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java2
-rw-r--r--services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java3
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java48
-rw-r--r--services/companion/java/com/android/server/companion/BackupRestoreProcessor.java7
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java10
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationDiskStore.java67
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java13
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/SecurityStateManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java5
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java10
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java4
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java151
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java52
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java20
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java16
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java46
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java37
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java108
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java111
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java13
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java50
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java14
-rw-r--r--services/core/java/com/android/server/location/fudger/LocationFudger.java63
-rw-r--r--services/core/java/com/android/server/location/fudger/LocationFudgerCache.java199
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java14
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java119
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java54
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java5
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java3
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java97
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java12
-rw-r--r--services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java16
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java21
-rw-r--r--services/core/java/com/android/server/vibrator/VendorVibrationSession.java41
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java6
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java98
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java2
-rw-r--r--services/core/java/com/android/server/webkit/SystemImpl.java6
-rw-r--r--services/core/java/com/android/server/webkit/SystemInterface.java3
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java59
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotationCoordinator.java22
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/SnapshotPersistQueue.java43
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java32
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java6
-rw-r--r--services/manifest_services.xml5
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java118
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java37
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java327
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java72
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java72
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java216
-rw-r--r--services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationCoordinatorTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java30
-rw-r--r--telephony/java/android/telephony/satellite/EarfcnRange.java5
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java11
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java16
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteInfo.java17
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java32
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java32
-rw-r--r--telephony/java/android/telephony/satellite/SatellitePosition.java9
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java3
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSessionStats.java3
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java64
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java58
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java18
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java7
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java10
-rw-r--r--telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java38
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl6
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt41
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml21
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java15
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java71
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java31
312 files changed, 7177 insertions, 1839 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f1906b574bb0..4302d24b3a72 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -23,6 +23,7 @@ aconfig_declarations_group {
"aconfig_mediacodec_flags_java_lib",
"aconfig_settingslib_flags_java_lib",
"aconfig_trade_in_mode_flags_java_lib",
+ "adpf_flags_java_lib",
"android.adaptiveauth.flags-aconfig-java",
"android.app.appfunctions.flags-aconfig-java",
"android.app.assist.flags-aconfig-java",
@@ -874,6 +875,13 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Adaptive Performance
+java_aconfig_library {
+ name: "adpf_flags_java_lib",
+ aconfig_declarations: "adpf_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Graphics
java_aconfig_library {
name: "hwui_flags_java_lib",
diff --git a/core/api/current.txt b/core/api/current.txt
index 4a1628e9ce39..1a34781f0102 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8917,6 +8917,7 @@ package android.app.assist {
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXTRA_APP_FUNCTION_DATA = "android.app.assist.extra.APP_FUNCTION_DATA";
+ field @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public static final String EXTRA_SESSION_TRANSFER_WEB_URI = "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
}
public class AssistStructure implements android.os.Parcelable {
@@ -9999,12 +10000,12 @@ package android.companion {
method public int describeContents();
method @Nullable public android.companion.AssociatedDevice getAssociatedDevice();
method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon();
+ method @FlaggedApi("android.companion.association_tag") @Nullable public android.companion.DeviceId getDeviceId();
method @Nullable public android.net.MacAddress getDeviceMacAddress();
method @Nullable public String getDeviceProfile();
method @Nullable public CharSequence getDisplayName();
method public int getId();
method public int getSystemDataSyncFlags();
- method @FlaggedApi("android.companion.association_tag") @Nullable public String getTag();
method public boolean isSelfManaged();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
@@ -10077,7 +10078,6 @@ package android.companion {
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void attachSystemDataTransport(int, @NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws android.companion.DeviceNotAssociatedException;
method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
- method @FlaggedApi("android.companion.association_tag") public void clearAssociationTag(int);
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
method public void disableSystemDataSyncForTypes(int, int);
method @Deprecated public void disassociate(@NonNull String);
@@ -10089,7 +10089,7 @@ package android.companion {
method @FlaggedApi("android.companion.perm_sync_user_consent") public boolean isPermissionTransferUserConsented(int);
method @FlaggedApi("android.companion.unpair_associated_device") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(int);
method public void requestNotificationAccess(android.content.ComponentName);
- method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
+ method @FlaggedApi("android.companion.association_tag") public void setDeviceId(int, @Nullable android.companion.DeviceId);
method @Deprecated @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
method @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull android.companion.ObservingDevicePresenceRequest);
method public void startSystemDataTransfer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.companion.CompanionException>) throws android.companion.DeviceNotAssociatedException;
@@ -10134,6 +10134,21 @@ package android.companion {
public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
}
+ @FlaggedApi("android.companion.association_tag") public final class DeviceId implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getCustomId();
+ method @Nullable public android.net.MacAddress getMacAddress();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.DeviceId> CREATOR;
+ }
+
+ public static final class DeviceId.Builder {
+ ctor public DeviceId.Builder();
+ method @NonNull public android.companion.DeviceId build();
+ method @NonNull public android.companion.DeviceId.Builder setCustomId(@Nullable String);
+ method @NonNull public android.companion.DeviceId.Builder setMacAddress(@Nullable android.net.MacAddress);
+ }
+
public class DeviceNotAssociatedException extends java.lang.RuntimeException {
}
@@ -24860,6 +24875,7 @@ package android.media {
method @Nullable public android.net.Uri getIconUri();
method @NonNull public String getId();
method @NonNull public CharSequence getName();
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.Set<java.lang.String> getRequiredPermissions();
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") public int getSupportedRoutingTypes();
method public int getType();
@@ -24928,6 +24944,7 @@ package android.media {
method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.Set<java.lang.String>);
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") @NonNull public android.media.MediaRoute2Info.Builder setSupportedRoutingTypes(int);
method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
@@ -37623,10 +37640,6 @@ package android.provider {
field public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
}
- @FlaggedApi("android.provider.new_default_account_api_enabled") public static class ContactsContract.LocalSimContactsWriteException extends java.lang.IllegalArgumentException {
- ctor public ContactsContract.LocalSimContactsWriteException(@NonNull String);
- }
-
public static final class ContactsContract.PhoneLookup implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.PhoneLookupColumns {
field public static final android.net.Uri CONTENT_FILTER_URI;
field public static final android.net.Uri ENTERPRISE_CONTENT_FILTER_URI;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7bfa878a7c0b..612a48ac00a7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -516,6 +516,7 @@ package android {
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultNotes = 17039429; // 0x1040045
+ field @FlaggedApi("android.permission.flags.cross_user_role_platform_api_enabled") public static final int config_defaultReservedForTestingProfileGroupExclusivity;
field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048
field public static final int config_defaultSms = 17039396; // 0x1040024
field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049
@@ -902,7 +903,7 @@ package android.app {
public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getAttributionTag();
- method @FlaggedApi("android.permission.flags.device_id_in_op_proxy_info_enabled") @Nullable public String getDeviceId();
+ method @FlaggedApi("android.permission.flags.device_id_in_op_proxy_info_enabled") @NonNull public String getDeviceId();
method @Nullable public String getPackageName();
method @IntRange(from=0) public int getUid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -7191,8 +7192,8 @@ package android.hardware.soundtrigger {
method public int getAudioCapabilities();
method @NonNull public byte[] getData();
method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphrases();
- method public boolean isAllowMultipleTriggers();
method public boolean isCaptureRequested();
+ method public boolean isMultipleTriggersAllowed();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.RecognitionConfig> CREATOR;
}
@@ -7200,11 +7201,11 @@ package android.hardware.soundtrigger {
public static final class SoundTrigger.RecognitionConfig.Builder {
ctor public SoundTrigger.RecognitionConfig.Builder();
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig build();
- method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAllowMultipleTriggers(boolean);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAudioCapabilities(int);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setCaptureRequested(boolean);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@NonNull byte[]);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setKeyphrases(@NonNull java.util.Collection<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
+ method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setMultipleTriggersAllowed(boolean);
}
public static class SoundTrigger.RecognitionEvent {
@@ -16090,6 +16091,10 @@ package android.telephony {
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
field public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44; // 0x2c
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43; // 0x2b
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED = 42; // 0x2a
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45; // 0x2d
field @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED = 47; // 0x2f
field @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
@@ -16134,6 +16139,13 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public default void onCallStatesChanged(@NonNull java.util.List<android.telephony.CallState>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static interface TelephonyCallback.CarrierRoamingNtnModeListener {
+ method public default void onCarrierRoamingNtnAvailableServicesChanged(@NonNull int[]);
+ method public default void onCarrierRoamingNtnEligibleStateChanged(boolean);
+ method public void onCarrierRoamingNtnModeChanged(boolean);
+ method public default void onCarrierRoamingNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrength);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") public static interface TelephonyCallback.CellularIdentifierDisclosedListener {
method public void onCellularIdentifierDisclosedChanged(@NonNull android.telephony.CellularIdentifierDisclosure);
}
@@ -16259,6 +16271,7 @@ package android.telephony {
method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackageName();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
+ method @FlaggedApi("com.android.internal.telephony.flags.get_group_id_level2") @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getGroupIdLevel2();
method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @NonNull @RequiresPermission(value=android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional=true) public java.util.List<java.lang.String> getImsPcscfAddresses();
method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @Nullable @RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER) public String getImsPrivateUserIdentity();
method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @NonNull @RequiresPermission(value=android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional=true) public java.util.List<android.net.Uri> getImsPublicUserIdentities();
@@ -18736,6 +18749,14 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class EarfcnRange implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=65535) public int getEndEarfcn();
+ method @IntRange(from=0, to=65535) public int getStartEarfcn();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.EarfcnRange> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public class EnableRequestAttributes {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isDemoMode();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isEmergencyMode();
@@ -18774,6 +18795,14 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteAccessConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.telephony.satellite.SatelliteInfo> getSatelliteInfos();
+ method @NonNull public java.util.List<java.lang.Integer> getTagIds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteAccessConfiguration> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteCapabilities implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public java.util.Map<java.lang.Integer,android.telephony.satellite.AntennaPosition> getAntennaPositionMap();
@@ -18788,6 +18817,11 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilities);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteCommunicationAllowedStateCallback {
+ method public default void onSatelliteAccessConfigurationChanged(@Nullable android.telephony.satellite.SatelliteAccessConfiguration);
+ method public void onSatelliteCommunicationAllowedStateChanged(boolean);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public byte[] getSatelliteDatagram();
@@ -18799,18 +18833,32 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.Integer> getBands();
+ method @NonNull public java.util.List<android.telephony.satellite.EarfcnRange> getEarfcnRanges();
+ method @NonNull public java.util.UUID getSatelliteId();
+ method @NonNull public android.telephony.satellite.SatellitePosition getSatellitePosition();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteInfo> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager {
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@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> getAttachRestrictionReasonsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCommunicationAllowedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCommunicationAllowedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForIncomingDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSupportedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -18823,16 +18871,21 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteSubscriberProvisionStatus(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @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>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCommunicationAllowedStateChanged(@NonNull android.telephony.satellite.SatelliteCommunicationAllowedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForIncomingDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSupportedStateChanged(@NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION = "android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED = "android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS = 7; // 0x7
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3; // 0x3
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5; // 0x5
@@ -18851,6 +18904,7 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DISPLAY_MODE_UNKNOWN = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT = "android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT";
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3; // 0x3
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
@@ -18917,18 +18971,100 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode();
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteModemEnableRequestAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriptionInfo getSatelliteSubscriptionInfo();
+ method public boolean isDemoMode();
+ method public boolean isEmergencyMode();
+ method public boolean isEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteModemEnableRequestAttributes> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteModemStateCallback {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatellitePosition implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getAltitudeKm();
+ method public double getLongitudeDegrees();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatellitePosition> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSatelliteSubscriptionProvisionStateChanged(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriberInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCarrierId();
+ method @NonNull public String getNiddApn();
+ method public int getSubId();
+ method @NonNull public String getSubscriberId();
+ method public int getSubscriberIdType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriberInfo> CREATOR;
+ field public static final int ICCID = 0; // 0x0
+ field public static final int IMSI_MSISDN = 1; // 0x1
+ }
+
+ public static final class SatelliteSubscriberInfo.Builder {
+ ctor public SatelliteSubscriberInfo.Builder();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo build();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setCarrierId(int);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setNiddApn(@NonNull String);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubId(int);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriberId(@NonNull String);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriberIdType(int);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriberProvisionStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo getSatelliteSubscriberInfo();
+ method public boolean isProvisioned();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriberProvisionStatus> CREATOR;
+ }
+
+ public static final class SatelliteSubscriberProvisionStatus.Builder {
+ ctor public SatelliteSubscriberProvisionStatus.Builder();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberProvisionStatus build();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberProvisionStatus.Builder setProvisioned(boolean);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberProvisionStatus.Builder setSatelliteSubscriberInfo(@NonNull android.telephony.satellite.SatelliteSubscriberInfo);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriptionInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getIccId();
+ method @NonNull public String getNiddApn();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriptionInfo> CREATOR;
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteSupportedStateCallback {
+ method public void onSatelliteSupportedStateChanged(boolean);
}
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onReceiveDatagramStateChanged(int, int, int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatellitePositionChanged(@NonNull android.telephony.satellite.PointingInfo);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSendDatagramRequested(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSendDatagramStateChanged(int, int, int);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSendDatagramStateChanged(int, int, int, int);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SystemSelectionSpecifier implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public int[] getBands();
+ method @NonNull public int[] getEarfcns();
+ method @NonNull public String getMccMnc();
+ method @NonNull public java.util.List<android.telephony.satellite.SatelliteInfo> getSatelliteInfos();
+ method @NonNull public int[] getTagIds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SystemSelectionSpecifier> CREATOR;
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3a62ac9e3c62..603677e89240 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -934,6 +934,7 @@ package android.companion {
method @NonNull public android.companion.AssociationInfo build();
method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice);
method @FlaggedApi("android.companion.association_device_icon") @NonNull public android.companion.AssociationInfo.Builder setDeviceIcon(@Nullable android.graphics.drawable.Icon);
+ method @FlaggedApi("android.companion.association_tag") @NonNull public android.companion.AssociationInfo.Builder setDeviceId(@Nullable android.companion.DeviceId);
method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress);
method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String);
method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence);
@@ -942,7 +943,6 @@ package android.companion {
method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean);
method @NonNull public android.companion.AssociationInfo.Builder setSelfManaged(boolean);
method @NonNull public android.companion.AssociationInfo.Builder setSystemDataSyncFlags(int);
- method @FlaggedApi("android.companion.association_tag") @NonNull public android.companion.AssociationInfo.Builder setTag(@Nullable String);
method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long);
}
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 009cd7249dcd..61b56877589b 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -21,6 +21,7 @@ import static android.app.TaskInfo.PROPERTY_VALUE_UNSET;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -69,6 +70,14 @@ public class AppCompatTaskInfo implements Parcelable {
public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET;
/**
+ * Contains the top activity bounds when the activity is letterboxed.
+ * It's {@code null} if there's no top activity in the task or it's not letterboxed.
+ */
+ // TODO(b/379824541) Remove duplicate information.
+ @Nullable
+ public Rect topActivityLetterboxBounds;
+
+ /**
* Stores camera-related app compat information about a particular Task.
*/
public CameraCompatTaskInfo cameraCompatTaskInfo = CameraCompatTaskInfo.create();
@@ -378,6 +387,7 @@ public class AppCompatTaskInfo implements Parcelable {
topActivityLetterboxHeight = source.readInt();
topActivityLetterboxAppWidth = source.readInt();
topActivityLetterboxAppHeight = source.readInt();
+ topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR);
cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR);
}
@@ -393,6 +403,7 @@ public class AppCompatTaskInfo implements Parcelable {
dest.writeInt(topActivityLetterboxHeight);
dest.writeInt(topActivityLetterboxAppWidth);
dest.writeInt(topActivityLetterboxAppHeight);
+ dest.writeTypedObject(topActivityLetterboxBounds, flags);
dest.writeTypedObject(cameraCompatTaskInfo, flags);
}
@@ -415,6 +426,7 @@ public class AppCompatTaskInfo implements Parcelable {
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
+ " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+ " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
+ + " topActivityLetterboxBounds=" + topActivityLetterboxBounds
+ " cameraCompatTaskInfo=" + cameraCompatTaskInfo.toString()
+ "}";
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 34765781d105..19138126698c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -3586,7 +3586,7 @@ public class AppOpsManager {
/** Attribution tag of the proxy that noted the op */
private @Nullable String mAttributionTag;
/** Persistent device Id of the proxy that noted the op */
- private @Nullable String mDeviceId;
+ private @NonNull String mDeviceId;
/**
* Reinit existing object with new state.
@@ -3599,7 +3599,7 @@ public class AppOpsManager {
* @hide
*/
public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable String deviceId) {
+ @Nullable String attributionTag, @NonNull String deviceId) {
mUid = Preconditions.checkArgumentNonnegative(uid);
mPackageName = packageName;
mAttributionTag = attributionTag;
@@ -3662,7 +3662,8 @@ public class AppOpsManager {
"from", 0);
this.mPackageName = packageName;
this.mAttributionTag = attributionTag;
- this.mDeviceId = deviceId;
+ this.mDeviceId = deviceId == null ? VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ : deviceId;
}
/**
* Copy constructor
@@ -3705,7 +3706,7 @@ public class AppOpsManager {
* Persistent device Id of the proxy that noted the op
*/
@FlaggedApi(Flags.FLAG_DEVICE_ID_IN_OP_PROXY_INFO_ENABLED)
- public @Nullable String getDeviceId() { return mDeviceId; }
+ public @NonNull String getDeviceId() { return mDeviceId; }
@Override
@DataClass.Generated.Member
@@ -3716,12 +3717,12 @@ public class AppOpsManager {
byte flg = 0;
if (mPackageName != null) flg |= 0x2;
if (mAttributionTag != null) flg |= 0x4;
- if (mDeviceId != null) flg |= 0x8;
+ flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mUid);
if (mPackageName != null) dest.writeString(mPackageName);
if (mAttributionTag != null) dest.writeString(mAttributionTag);
- if (mDeviceId != null) dest.writeString(mDeviceId);
+ dest.writeString(mDeviceId);
}
@Override
@@ -3739,7 +3740,8 @@ public class AppOpsManager {
int uid = in.readInt();
String packageName = (flg & 0x2) == 0 ? null : in.readString();
String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
- String deviceId = (flg & 0x8) == 0 ? null : in.readString();
+ String deviceId = (flg & 0x8) == 0 ? VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ : in.readString();
this.mUid = uid;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mUid,
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 3cbea87e135e..da338474a448 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -798,7 +798,7 @@ public class ApplicationPackageManager extends PackageManager {
private final static PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>
mHasSystemFeatureCache = new PropertyInvalidatedCache<>(
new PropertyInvalidatedCache.Args(MODULE_SYSTEM)
- .api(HAS_SYSTEM_FEATURE_API).maxEntries(256).isolateUids(false),
+ .api(HAS_SYSTEM_FEATURE_API).maxEntries(SDK_FEATURE_COUNT).isolateUids(false),
HAS_SYSTEM_FEATURE_API, null) {
@Override
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index 43a46ba7885d..3e3ca2488bd3 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -30,6 +30,31 @@ public class AssistContent implements Parcelable {
public static final String EXTRA_APP_FUNCTION_DATA =
"android.app.assist.extra.APP_FUNCTION_DATA";
+ /**
+ * This extra can be optionally supplied in the {@link #getExtras} bundle to provide a
+ * {@link Uri} which will be utilized when transitioning a user's session to another surface.
+ *
+ * <p>If provided, instead of using the URI provided in {@link #setWebUri}, the
+ * "Open in browser" feature will use this URI to transition the current session from one
+ * surface to the other. Apps may choose to encode session or user information into this
+ * URI in order to provide a better session transfer experience.
+ *
+ * <p>Unlike {@link #setWebUri}, this URI will not be used for features where the user might
+ * accidentally share it with another user. However, developers should not encode
+ * authentication credentials into this URI, because it will be surfaced in the browser URL
+ * bar and may be copied and shared from there.
+ *
+ * <p>When providing this extra, developers should still continue to provide
+ * {@link #setWebUri} for backwards compatibility with features such as
+ * <a href="https://developer.android.com/guide/components/activities/recents#url-sharing">
+ * recents URL sharing</a> which do not benefit from a session-transfer web URI.
+ *
+ * @see android.app.Activity#requestOpenInBrowserEducation()
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+ public static final String EXTRA_SESSION_TRANSFER_WEB_URI =
+ "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mIsAppProvidedIntent = false;
private boolean mIsAppProvidedWebUri = false;
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 124973489dd1..2e108a1eed2c 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -54,8 +54,6 @@ public final class AssociationInfo implements Parcelable {
@NonNull
private final String mPackageName;
@Nullable
- private final String mTag;
- @Nullable
private final MacAddress mDeviceMacAddress;
@Nullable
private final CharSequence mDisplayName;
@@ -85,6 +83,8 @@ public final class AssociationInfo implements Parcelable {
*/
private final long mLastTimeConnectedMs;
private final int mSystemDataSyncFlags;
+ @Nullable
+ private final DeviceId mDeviceId;
/**
* A device icon displayed on a selfManaged association dialog.
@@ -97,11 +97,11 @@ public final class AssociationInfo implements Parcelable {
* @hide
*/
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
- @Nullable String tag, @Nullable MacAddress macAddress,
- @Nullable CharSequence displayName, @Nullable String deviceProfile,
- @Nullable AssociatedDevice associatedDevice, boolean selfManaged,
- boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs,
- long lastTimeConnectedMs, int systemDataSyncFlags, @Nullable Icon deviceIcon) {
+ @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
+ @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
+ boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, boolean pending,
+ long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags,
+ @Nullable Icon deviceIcon, @Nullable DeviceId deviceId) {
if (id <= 0) {
throw new IllegalArgumentException("Association ID should be greater than 0");
}
@@ -115,7 +115,6 @@ public final class AssociationInfo implements Parcelable {
mPackageName = packageName;
mDeviceMacAddress = macAddress;
mDisplayName = displayName;
- mTag = tag;
mDeviceProfile = deviceProfile;
mAssociatedDevice = associatedDevice;
mSelfManaged = selfManaged;
@@ -126,6 +125,7 @@ public final class AssociationInfo implements Parcelable {
mLastTimeConnectedMs = lastTimeConnectedMs;
mSystemDataSyncFlags = systemDataSyncFlags;
mDeviceIcon = deviceIcon;
+ mDeviceId = deviceId;
}
/**
@@ -155,13 +155,13 @@ public final class AssociationInfo implements Parcelable {
}
/**
- * @return the tag of this association.
- * @see CompanionDeviceManager#setAssociationTag(int, String)
+ * @return the {@link DeviceId} of this association.
+ * @see CompanionDeviceManager#setDeviceId(int, DeviceId)
*/
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@Nullable
- public String getTag() {
- return mTag;
+ public DeviceId getDeviceId() {
+ return mDeviceId;
}
/**
@@ -355,7 +355,6 @@ public final class AssociationInfo implements Parcelable {
+ "mId=" + mId
+ ", mUserId=" + mUserId
+ ", mPackageName='" + mPackageName + '\''
- + ", mTag='" + mTag + '\''
+ ", mDeviceMacAddress=" + mDeviceMacAddress
+ ", mDisplayName='" + mDisplayName + '\''
+ ", mDeviceProfile='" + mDeviceProfile + '\''
@@ -369,6 +368,7 @@ public final class AssociationInfo implements Parcelable {
mLastTimeConnectedMs == Long.MAX_VALUE
? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs))
+ ", mSystemDataSyncFlags=" + mSystemDataSyncFlags
+ + ", mDeviceId='" + mDeviceId
+ '}';
}
@@ -386,21 +386,22 @@ public final class AssociationInfo implements Parcelable {
&& mTimeApprovedMs == that.mTimeApprovedMs
&& mLastTimeConnectedMs == that.mLastTimeConnectedMs
&& Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mTag, that.mTag)
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mDisplayName, that.mDisplayName)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
&& mSystemDataSyncFlags == that.mSystemDataSyncFlags
&& (mDeviceIcon == null ? that.mDeviceIcon == null
- : mDeviceIcon.sameAs(that.mDeviceIcon));
+ : mDeviceIcon.sameAs(that.mDeviceIcon))
+ && Objects.equals(mDeviceId, that.mDeviceId);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mUserId, mPackageName, mTag, mDeviceMacAddress, mDisplayName,
+ return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
- mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon);
+ mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon,
+ mDeviceId);
}
@Override
@@ -413,7 +414,6 @@ public final class AssociationInfo implements Parcelable {
dest.writeInt(mId);
dest.writeInt(mUserId);
dest.writeString(mPackageName);
- dest.writeString(mTag);
dest.writeTypedObject(mDeviceMacAddress, 0);
dest.writeCharSequence(mDisplayName);
dest.writeString(mDeviceProfile);
@@ -431,13 +431,19 @@ public final class AssociationInfo implements Parcelable {
} else {
dest.writeInt(0);
}
+
+ if (Flags.associationTag() && mDeviceId != null) {
+ dest.writeInt(1);
+ dest.writeTypedObject(mDeviceId, flags);
+ } else {
+ dest.writeInt(0);
+ }
}
private AssociationInfo(@NonNull Parcel in) {
mId = in.readInt();
mUserId = in.readInt();
mPackageName = in.readString();
- mTag = in.readString();
mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
mDisplayName = in.readCharSequence();
mDeviceProfile = in.readString();
@@ -454,6 +460,12 @@ public final class AssociationInfo implements Parcelable {
} else {
mDeviceIcon = null;
}
+
+ if (Flags.associationTag() && in.readInt() == 1) {
+ mDeviceId = in.readTypedObject(DeviceId.CREATOR);
+ } else {
+ mDeviceId = null;
+ }
}
@NonNull
@@ -481,7 +493,6 @@ public final class AssociationInfo implements Parcelable {
private final int mId;
private final int mUserId;
private final String mPackageName;
- private String mTag;
private MacAddress mDeviceMacAddress;
private CharSequence mDisplayName;
private String mDeviceProfile;
@@ -494,6 +505,7 @@ public final class AssociationInfo implements Parcelable {
private long mLastTimeConnectedMs;
private int mSystemDataSyncFlags;
private Icon mDeviceIcon;
+ private DeviceId mDeviceId;
/** @hide */
@TestApi
@@ -509,7 +521,6 @@ public final class AssociationInfo implements Parcelable {
mId = info.mId;
mUserId = info.mUserId;
mPackageName = info.mPackageName;
- mTag = info.mTag;
mDeviceMacAddress = info.mDeviceMacAddress;
mDisplayName = info.mDisplayName;
mDeviceProfile = info.mDeviceProfile;
@@ -522,6 +533,7 @@ public final class AssociationInfo implements Parcelable {
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
mDeviceIcon = info.mDeviceIcon;
+ mDeviceId = info.mDeviceId;
}
/**
@@ -534,7 +546,6 @@ public final class AssociationInfo implements Parcelable {
mId = id;
mUserId = userId;
mPackageName = packageName;
- mTag = info.mTag;
mDeviceMacAddress = info.mDeviceMacAddress;
mDisplayName = info.mDisplayName;
mDeviceProfile = info.mDeviceProfile;
@@ -547,14 +558,15 @@ public final class AssociationInfo implements Parcelable {
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
mDeviceIcon = info.mDeviceIcon;
+ mDeviceId = info.mDeviceId;
}
/** @hide */
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@TestApi
@NonNull
- public Builder setTag(@Nullable String tag) {
- mTag = tag;
+ public Builder setDeviceId(@Nullable DeviceId deviceId) {
+ mDeviceId = deviceId;
return this;
}
@@ -684,7 +696,6 @@ public final class AssociationInfo implements Parcelable {
mId,
mUserId,
mPackageName,
- mTag,
mDeviceMacAddress,
mDisplayName,
mDeviceProfile,
@@ -696,7 +707,8 @@ public final class AssociationInfo implements Parcelable {
mTimeApprovedMs,
mLastTimeConnectedMs,
mSystemDataSyncFlags,
- mDeviceIcon
+ mDeviceIcon,
+ mDeviceId
);
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 04668479d8e3..a96ba11eb482 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -278,12 +278,6 @@ public final class CompanionDeviceManager {
public static final int MESSAGE_ONEWAY_TO_WEARABLE = 0x43847987; // +TOW
/**
- * The length limit of Association tag.
- * @hide
- */
- private static final int ASSOCIATION_TAG_LENGTH_LIMIT = 1024;
-
- /**
* Callback for applications to receive updates about and the outcome of
* {@link AssociationRequest} issued via {@code associate()} call.
*
@@ -1780,57 +1774,25 @@ public final class CompanionDeviceManager {
}
/**
- * Sets the {@link AssociationInfo#getTag() tag} for this association.
- *
- * <p>The length of the tag must be at most 1024 characters to save disk space.
- *
- * <p>This allows to store useful information about the associated devices.
- *
- * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association
- * of the companion device recorded by CompanionDeviceManager
- * @param tag the tag of this association
- */
- @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
- @UserHandleAware
- public void setAssociationTag(int associationId, @NonNull String tag) {
- if (mService == null) {
- Log.w(TAG, "CompanionDeviceManager service is not available.");
- return;
- }
-
- Objects.requireNonNull(tag, "tag cannot be null");
-
- if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) {
- throw new IllegalArgumentException("Length of the tag must be at most"
- + ASSOCIATION_TAG_LENGTH_LIMIT + " characters");
- }
-
- try {
- mService.setAssociationTag(associationId, tag);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Clears the {@link AssociationInfo#getTag() tag} for this association.
+ * Sets the {@link DeviceId deviceId} for this association.
*
- * <p>The tag will be set to null for this association when calling this API.
+ * <p>This device id helps the system uniquely identify your device for efficient device
+ * management and prevents duplicate entries.
*
* @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association
- * of the companion device recorded by CompanionDeviceManager
- * @see CompanionDeviceManager#setAssociationTag(int, String)
+ * of the companion device recorded by CompanionDeviceManager.
+ * @param deviceId to be used as device identifier to represent the associated device.
*/
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@UserHandleAware
- public void clearAssociationTag(int associationId) {
+ public void setDeviceId(int associationId, @Nullable DeviceId deviceId) {
if (mService == null) {
Log.w(TAG, "CompanionDeviceManager service is not available.");
return;
}
try {
- mService.clearAssociationTag(associationId);
+ mService.setDeviceId(associationId, deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/DeviceId.aidl b/core/java/android/companion/DeviceId.aidl
new file mode 100644
index 000000000000..d60d5f40eb6a
--- /dev/null
+++ b/core/java/android/companion/DeviceId.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+parcelable DeviceId; \ No newline at end of file
diff --git a/core/java/android/companion/DeviceId.java b/core/java/android/companion/DeviceId.java
new file mode 100644
index 000000000000..f66a1ae5c175
--- /dev/null
+++ b/core/java/android/companion/DeviceId.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.OneTimeUseBuilder;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A device id represents a device identifier managed by the companion app.
+ */
+@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
+public final class DeviceId implements Parcelable {
+ /**
+ * The length limit of custom id.
+ */
+ private static final int CUSTOM_ID_LENGTH_LIMIT = 1024;
+
+ private final String mCustomId;
+ private final MacAddress mMacAddress;
+
+ /**
+ * @hide
+ */
+ public DeviceId(@Nullable String customId, @Nullable MacAddress macAddress) {
+ mCustomId = customId;
+ mMacAddress = macAddress;
+ }
+
+ /**
+ * Returns true if two Device ids are represent the same device. False otherwise.
+ * @hide
+ */
+ public boolean isSameDevice(@Nullable DeviceId other) {
+ if (other == null) {
+ return false;
+ }
+
+ if (this.mCustomId != null && other.mCustomId != null) {
+ return this.mCustomId.equals(other.mCustomId);
+ }
+ if (this.mMacAddress != null && other.mMacAddress != null) {
+ return this.mMacAddress.equals(other.mMacAddress);
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ @Nullable
+ public String getMacAddressAsString() {
+ return mMacAddress != null ? mMacAddress.toString().toUpperCase(Locale.US) : null;
+ }
+
+ /**
+ * @return the custom id that managed by the companion app.
+ */
+ @Nullable
+ public String getCustomId() {
+ return mCustomId;
+ }
+
+ /**
+ * @return the mac address that managed by the companion app.
+ */
+ @Nullable
+ public MacAddress getMacAddress() {
+ return mMacAddress;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mCustomId != null) {
+ dest.writeInt(1);
+ dest.writeString8(mCustomId);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeTypedObject(mMacAddress, 0);
+
+ }
+
+ private DeviceId(@NonNull Parcel in) {
+ int flg = in.readInt();
+ if (flg == 1) {
+ mCustomId = in.readString8();
+ } else {
+ mCustomId = null;
+ }
+ mMacAddress = in.readTypedObject(MacAddress.CREATOR);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<DeviceId> CREATOR =
+ new Parcelable.Creator<DeviceId>() {
+ @Override
+ public DeviceId[] newArray(int size) {
+ return new DeviceId[size];
+ }
+
+ @Override
+ public DeviceId createFromParcel(@android.annotation.NonNull Parcel in) {
+ return new DeviceId(in);
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCustomId, mMacAddress);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DeviceId that)) return false;
+
+ return Objects.equals(mCustomId, that.mCustomId)
+ && Objects.equals(mMacAddress, that.mMacAddress);
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceId{"
+ + "," + "mCustomId= " + mCustomId
+ + "," + "mMacAddress= " + mMacAddress
+ + "}";
+ }
+
+ /**
+ * A builder for {@link DeviceId}
+ */
+ public static final class Builder extends OneTimeUseBuilder<DeviceId> {
+ private String mCustomId;
+ private MacAddress mMacAddress;
+
+ public Builder() {}
+
+ /**
+ * Sets the custom device id. This id is used by the Companion app to
+ * identify a specific device.
+ *
+ * @param customId the custom device id
+ * @throws IllegalArgumentException length of the custom id must more than 1024
+ * characters to save disk space.
+ */
+ @NonNull
+ public Builder setCustomId(@Nullable String customId) {
+ checkNotUsed();
+ if (customId != null
+ && customId.length() > CUSTOM_ID_LENGTH_LIMIT) {
+ throw new IllegalArgumentException("Length of the custom id must be at most "
+ + CUSTOM_ID_LENGTH_LIMIT + " characters");
+ }
+ this.mCustomId = customId;
+ return this;
+ }
+
+ /**
+ * Sets the mac address. This mac address is used by the Companion app to
+ * identify a specific device.
+ *
+ * @param macAddress the remote device mac address
+ * @throws IllegalArgumentException length of the custom id must more than 1024
+ * characters to save disk space.
+ */
+ @NonNull
+ public Builder setMacAddress(@Nullable MacAddress macAddress) {
+ checkNotUsed();
+ mMacAddress = macAddress;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public DeviceId build() {
+ markUsed();
+ if (mCustomId == null && mMacAddress == null) {
+ throw new IllegalArgumentException("At least one device id property must be"
+ + "non-null to build a DeviceId.");
+ }
+ return new DeviceId(mCustomId, mMacAddress);
+ }
+ }
+}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index de3ddec05d27..a2b7dd9c3d0e 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -28,6 +28,7 @@ import android.companion.ObservingDevicePresenceRequest;
import android.companion.datatransfer.PermissionSyncRequest;
import android.content.ComponentName;
import android.os.ParcelUuid;
+import android.companion.DeviceId;
/**
@@ -134,9 +135,7 @@ interface ICompanionDeviceManager {
@EnforcePermission("MANAGE_COMPANION_DEVICES")
void enableSecureTransport(boolean enabled);
- void setAssociationTag(int associationId, String tag);
-
- void clearAssociationTag(int associationId);
+ void setDeviceId(int associationId, in DeviceId deviceId);
byte[] getBackupPayload(int userId);
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 117d8fe24809..3a74130d5e83 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -1267,6 +1267,9 @@ public final class ContextHubManager {
* registration succeeds, the endpoint can receive notifications through the provided callback.
*
* @param hubEndpoint {@link HubEndpoint} object created by {@link HubEndpoint.Builder}
+ * @throws IllegalStateException if the registration failed, for example if too many endpoints
+ * are registered at the service
+ * @throws UnsupportedOperationException if endpoint registration is not supported
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 2ba107805569..73c8e3e130a1 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -157,7 +157,7 @@ public class ConversionUtil {
SoundTrigger.RecognitionConfig apiConfig) {
RecognitionConfig aidlConfig = new RecognitionConfig();
aidlConfig.captureRequested = apiConfig.isCaptureRequested();
- // apiConfig.isAllowMultipleTriggers() is ignored by the lower layers.
+ // apiConfig.isMultipleTriggersAllowed() is ignored by the lower layers.
aidlConfig.phraseRecognitionExtras =
new PhraseRecognitionExtra[apiConfig.getKeyphrases().size()];
for (int i = 0; i < apiConfig.getKeyphrases().size(); ++i) {
@@ -178,7 +178,7 @@ public class ConversionUtil {
}
return new SoundTrigger.RecognitionConfig.Builder()
.setCaptureRequested(aidlConfig.captureRequested)
- .setAllowMultipleTriggers(false)
+ .setMultipleTriggersAllowed(false)
.setKeyphrases(keyphrases)
.setData(Arrays.copyOf(aidlConfig.data, aidlConfig.data.length))
.setAudioCapabilities(aidl2apiAudioCapabilities(aidlConfig.audioCapabilities))
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 7745b036bcbe..7c4ddc669968 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1518,7 +1518,7 @@ public class SoundTrigger {
@FlaggedApi(android.media.soundtrigger.Flags.FLAG_MANAGER_API)
public static final class RecognitionConfig implements Parcelable {
private final boolean mCaptureRequested;
- private final boolean mAllowMultipleTriggers;
+ private final boolean mMultipleTriggersAllowed;
private final KeyphraseRecognitionExtra mKeyphrases[];
private final byte[] mData;
private final @ModuleProperties.AudioCapabilities int mAudioCapabilities;
@@ -1529,7 +1529,7 @@ public class SoundTrigger {
* {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)}
*
* @param captureRequested Whether the DSP should capture the trigger sound.
- * @param allowMultipleTriggers Whether the service should restart listening after the DSP
+ * @param multipleTriggersAllowed Whether the service should restart listening after the DSP
* triggers.
* @param keyphrases List of keyphrases in the sound model.
* @param data Opaque data for use by system applications who know about voice engine
@@ -1537,11 +1537,11 @@ public class SoundTrigger {
* @param audioCapabilities Bit field encoding of the AudioCapabilities. See
* {@link ModuleProperties.AudioCapabilities} for details.
*/
- private RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
+ private RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed,
@Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
@ModuleProperties.AudioCapabilities int audioCapabilities) {
this.mCaptureRequested = captureRequested;
- this.mAllowMultipleTriggers = allowMultipleTriggers;
+ this.mMultipleTriggersAllowed = multipleTriggersAllowed;
this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
this.mData = data != null ? data : new byte[0];
this.mAudioCapabilities = audioCapabilities;
@@ -1553,7 +1553,7 @@ public class SoundTrigger {
*
* @deprecated Use {@link Builder} instead.
* @param captureRequested Whether the DSP should capture the trigger sound.
- * @param allowMultipleTriggers Whether the service should restart listening after the DSP
+ * @param multipleTriggersAllowed Whether the service should restart listening after the DSP
* triggers.
* @param keyphrases List of keyphrases in the sound model.
* @param data Opaque data for use by system applications.
@@ -1563,9 +1563,9 @@ public class SoundTrigger {
@UnsupportedAppUsage
@Deprecated
@TestApi
- public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
+ public RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed,
@Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
- this(captureRequested, allowMultipleTriggers, keyphrases, data, 0);
+ this(captureRequested, multipleTriggersAllowed, keyphrases, data, 0);
}
public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
@@ -1593,8 +1593,8 @@ public class SoundTrigger {
* <p><b>Note:</b> This config flag is currently used at the service layer rather than by
* the DSP.
*/
- public boolean isAllowMultipleTriggers() {
- return mAllowMultipleTriggers;
+ public boolean isMultipleTriggersAllowed() {
+ return mMultipleTriggersAllowed;
}
/**
@@ -1627,19 +1627,19 @@ public class SoundTrigger {
private static RecognitionConfig fromParcel(Parcel in) {
boolean captureRequested = in.readBoolean();
- boolean allowMultipleTriggers = in.readBoolean();
+ boolean multipleTriggersAllowed = in.readBoolean();
KeyphraseRecognitionExtra[] keyphrases =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
byte[] data = in.createByteArray();
int audioCapabilities = in.readInt();
- return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
- audioCapabilities);
+ return new RecognitionConfig(captureRequested, multipleTriggersAllowed, keyphrases,
+ data, audioCapabilities);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBoolean(mCaptureRequested);
- dest.writeBoolean(mAllowMultipleTriggers);
+ dest.writeBoolean(mMultipleTriggersAllowed);
dest.writeTypedArray(mKeyphrases, flags);
dest.writeByteArray(mData);
dest.writeInt(mAudioCapabilities);
@@ -1653,7 +1653,7 @@ public class SoundTrigger {
@Override
public String toString() {
return "RecognitionConfig [captureRequested=" + mCaptureRequested
- + ", allowMultipleTriggers=" + mAllowMultipleTriggers + ", keyphrases="
+ + ", multipleTriggersAllowed=" + mMultipleTriggersAllowed + ", keyphrases="
+ Arrays.toString(mKeyphrases) + ", data=" + Arrays.toString(mData)
+ ", audioCapabilities=" + Integer.toHexString(mAudioCapabilities) + "]";
}
@@ -1670,7 +1670,7 @@ public class SoundTrigger {
if (mCaptureRequested != other.mCaptureRequested) {
return false;
}
- if (mAllowMultipleTriggers != other.mAllowMultipleTriggers) {
+ if (mMultipleTriggersAllowed != other.mMultipleTriggersAllowed) {
return false;
}
if (!Arrays.equals(mKeyphrases, other.mKeyphrases)) {
@@ -1690,7 +1690,7 @@ public class SoundTrigger {
final int prime = 31;
int result = 1;
result = prime * result + (mCaptureRequested ? 1 : 0);
- result = prime * result + (mAllowMultipleTriggers ? 1 : 0);
+ result = prime * result + (mMultipleTriggersAllowed ? 1 : 0);
result = prime * result + Arrays.hashCode(mKeyphrases);
result = prime * result + Arrays.hashCode(mData);
result = prime * result + mAudioCapabilities;
@@ -1702,7 +1702,7 @@ public class SoundTrigger {
*/
public static final class Builder {
private boolean mCaptureRequested;
- private boolean mAllowMultipleTriggers;
+ private boolean mMultipleTriggersAllowed;
@Nullable private KeyphraseRecognitionExtra[] mKeyphrases;
@Nullable private byte[] mData;
private @ModuleProperties.AudioCapabilities int mAudioCapabilities;
@@ -1725,12 +1725,12 @@ public class SoundTrigger {
/**
* Sets allow multiple triggers state.
- * @param allowMultipleTriggers Whether the service should restart listening after the
+ * @param multipleTriggersAllowed Whether the service should restart listening after the
* DSP triggers.
* @return the same Builder instance.
*/
- public @NonNull Builder setAllowMultipleTriggers(boolean allowMultipleTriggers) {
- mAllowMultipleTriggers = allowMultipleTriggers;
+ public @NonNull Builder setMultipleTriggersAllowed(boolean multipleTriggersAllowed) {
+ mMultipleTriggersAllowed = multipleTriggersAllowed;
return this;
}
@@ -1779,7 +1779,7 @@ public class SoundTrigger {
public @NonNull RecognitionConfig build() {
RecognitionConfig config = new RecognitionConfig(
/* captureRequested= */ mCaptureRequested,
- /* allowMultipleTriggers= */ mAllowMultipleTriggers,
+ /* multipleTriggersAllowed= */ mMultipleTriggersAllowed,
/* keyphrases= */ mKeyphrases,
/* data= */ mData,
/* audioCapabilities= */ mAudioCapabilities);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9b39c62ad03e..8b6da7e0ae58 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -41,6 +41,8 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.View;
+import com.android.internal.util.FrameworkStatsLog;
+
import dalvik.system.VMRuntime;
import java.lang.annotation.Retention;
@@ -1666,11 +1668,14 @@ public class Build {
*/
@FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
public static @BackportedFixStatus int getBackportedFixStatus(long id) {
- if (id <= 0 || id > 1023) {
- return BACKPORTED_FIX_STATUS_UNKNOWN;
+ @BackportedFixStatus int status = BACKPORTED_FIX_STATUS_UNKNOWN;
+ int uid = Binder.getCallingUid();
+ if (id > 0 && id <= 1023) {
+ status = isBitSet(BackportedFixesProperties.alias_bitset(), (int) id)
+ ? BACKPORTED_FIX_STATUS_FIXED : BACKPORTED_FIX_STATUS_UNKNOWN;
}
- return isBitSet(BackportedFixesProperties.alias_bitset(), (int) id)
- ? BACKPORTED_FIX_STATUS_FIXED : BACKPORTED_FIX_STATUS_UNKNOWN;
+ FrameworkStatsLog.write(FrameworkStatsLog.BACKPORTED_FIX_STATUS_REPORTED, uid, id, status);
+ return status;
}
private static boolean isBitSet(List<Long> bitsetLongArray, int bitIndex) {
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 476968151e18..23114c4318c7 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -30,6 +30,8 @@ import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
+
import dalvik.annotation.optimization.NeverCompile;
import java.io.FileDescriptor;
@@ -116,39 +118,58 @@ public final class MessageQueue {
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
- if (sIsProcessAllowedToUseConcurrent == null) {
- // Concurrent mode modifies behavior that is observable via reflection and is commonly
- // used by tests.
- // For now, we limit it to system processes to avoid breaking apps and their tests.
- boolean useConcurrent = UserHandle.isCore(Process.myUid());
+ initIsProcessAllowedToUseConcurrent();
+ mUseConcurrent = sIsProcessAllowedToUseConcurrent;
+ mQuitAllowed = quitAllowed;
+ mPtr = nativeInit();
+ }
- // Some platform tests run in system UIDs.
- // Use this awful heuristic to detect them.
- if (useConcurrent) {
- final String processName = Process.myProcessName();
- if (processName == null
- || processName.contains("test")
- || processName.contains("Test")) {
- useConcurrent = false;
- }
- }
+ private static void initIsProcessAllowedToUseConcurrent() {
+ if (sIsProcessAllowedToUseConcurrent != null) {
+ return;
+ }
- // We can lift this restriction in the future after we've made it possible for test
- // authors to test Looper and MessageQueue without resorting to reflection.
+ if (RavenwoodEnvironment.getInstance().isRunningOnRavenwood()) {
+ sIsProcessAllowedToUseConcurrent = false;
+ return;
+ }
- // Holdback study.
- if (useConcurrent && Flags.messageQueueForceLegacy()) {
- useConcurrent = false;
- }
+ final String processName = Process.myProcessName();
+ if (processName == null) {
+ // Assume that this is a host-side test and avoid concurrent mode for now.
+ sIsProcessAllowedToUseConcurrent = false;
+ return;
+ }
- sIsProcessAllowedToUseConcurrent = useConcurrent;
- mUseConcurrent = useConcurrent;
+ // Concurrent mode modifies behavior that is observable via reflection and is commonly
+ // used by tests.
+ // For now, we limit it to system processes to avoid breaking apps and their tests.
+ sIsProcessAllowedToUseConcurrent = UserHandle.isCore(Process.myUid());
+
+ if (sIsProcessAllowedToUseConcurrent) {
+ // Some platform tests run in core UIDs.
+ // Use this awful heuristic to detect them.
+ if (processName.contains("test") || processName.contains("Test")) {
+ sIsProcessAllowedToUseConcurrent = false;
+ }
} else {
- mUseConcurrent = sIsProcessAllowedToUseConcurrent;
+ // Also explicitly allow SystemUI processes.
+ // SystemUI doesn't run in a core UID, but we want to give it the performance boost,
+ // and we know that it's safe to use the concurrent implementation in SystemUI.
+ sIsProcessAllowedToUseConcurrent =
+ processName.equals("com.android.systemui")
+ || processName.startsWith("com.android.systemui:");
+ // On Android distributions where SystemUI has a different process name,
+ // the above condition may need to be adjusted accordingly.
}
- mQuitAllowed = quitAllowed;
- mPtr = nativeInit();
+ // We can lift these restrictions in the future after we've made it possible for test
+ // authors to test Looper and MessageQueue without resorting to reflection.
+
+ // Holdback study.
+ if (sIsProcessAllowedToUseConcurrent && Flags.messageQueueForceLegacy()) {
+ sIsProcessAllowedToUseConcurrent = false;
+ }
}
@Override
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index f1936b5e0ff9..4a14a8d0faf8 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -64,4 +64,10 @@ interface IHintManager {
* Get Maximum number of graphics pipeline threads allowed per-app.
*/
int getMaxGraphicsPipelineThreadsCount();
+
+ /**
+ * Used by the JNI to pass an interface to the SessionManager;
+ * for internal use only.
+ */
+ oneway void passSessionManagerBinder(in IBinder sessionManager);
}
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index 6fd4f3c7c01a..e3f899de6d01 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -27,4 +27,9 @@ oneway interface IHintSession {
void sendHint(int hint);
void setMode(int mode, boolean enabled);
void reportActualWorkDuration2(in WorkDuration[] workDurations);
+
+ /**
+ * Used by apps to associate a session to a given set of layers
+ */
+ oneway void associateToLayers(in IBinder[] layerTokens);
}
diff --git a/core/java/android/os/SessionCreationConfig.aidl b/core/java/android/os/SessionCreationConfig.aidl
index cdc0ef461e0c..17147e43cf83 100644
--- a/core/java/android/os/SessionCreationConfig.aidl
+++ b/core/java/android/os/SessionCreationConfig.aidl
@@ -36,4 +36,12 @@ parcelable SessionCreationConfig {
* List of the modes to be enabled upon session creation.
*/
SessionMode[] modesToEnable;
+
+ /**
+ * List of layers to attach this session to.
+ *
+ * Note: DO NOT STORE THESE IN HintSessionManager, as
+ * it will break the layer lifecycle.
+ */
+ IBinder[] layerTokens;
}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 0bd301c05b3d..c2b8157de416 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -465,3 +465,12 @@ flag {
description: "Enables text classifier for OTP detection that is updatable from mainline module"
bug: "377229653"
}
+
+flag {
+ name: "cross_user_role_platform_api_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Enable cross-user roles platform API"
+ bug: "367732307"
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8e379e82a5fb..99ff38b43adc 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -10859,28 +10859,6 @@ public final class ContactsContract {
"vnd.android.cursor.item/contact_metadata_sync_state";
}
- /**
- * This exception is thrown when an attempt is made to perform a write operation
- * on a contact or contact group targeting a local account or a SIM account,
- * and the operation is not permitted under the current conditions.
- * The local account can be retrieved using {@link RawContacts#getLocalAccountName(Context)}
- * and {@link RawContacts#getLocalAccountType(Context)}.
- * SIM accounts can be retrieved using {@link SimContacts#getSimAccounts(ContentResolver)}.
- *
- * <p>Local and SIM accounts have limitations that may prevent write operations
- * due to their nature, underlying implementation, or the current system state.
- * For example, the SIM card may be full, read-only, or not present.
- *
- * <p>The specific conditions under which write operations are permitted on
- * local or SIM accounts can vary.
- */
- @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
- public static class LocalSimContactsWriteException extends IllegalArgumentException {
- public LocalSimContactsWriteException(@NonNull String s) {
- super(s);
- }
- }
-
private static Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull Uri uri,
@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
try (ContentProviderClient client = resolver.acquireContentProviderClient(uri)) {
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index ec7e3a53f295..42dbd3786001 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -105,6 +105,13 @@ flag {
}
flag {
+ name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected_r_w"
+ namespace: "responsible_apis"
+ description: "Prevent intent redirect attacks by showing a toast if not yet collected"
+ bug: "361143368"
+}
+
+flag {
name: "prevent_intent_redirect_throw_exception_if_nested_keys_not_collected"
namespace: "responsible_apis"
description: "Prevent intent redirect attacks by throwing exception if the intent does not collect nested keys"
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 5d0ec73a024b..72569075c2ed 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -2363,7 +2363,6 @@ public abstract class NotificationListenerService extends Service {
// -- parcelable interface --
private RankingMap(Parcel in) {
- final ClassLoader cl = getClass().getClassLoader();
final int count = in.readInt();
mOrderedKeys.ensureCapacity(count);
mRankings.ensureCapacity(count);
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 7d79fd3d44ea..68fd11592fee 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -1541,7 +1541,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mInternalCallback,
new RecognitionConfig.Builder()
.setCaptureRequested(captureTriggerAudio)
- .setAllowMultipleTriggers(allowMultipleTriggers)
+ .setMultipleTriggersAllowed(allowMultipleTriggers)
.setKeyphrases(recognitionExtra)
.setData(data)
.setAudioCapabilities(audioCapabilities)
diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS
index 5f9f6bde3129..b6f0270dfbbc 100644
--- a/core/java/android/service/voice/OWNERS
+++ b/core/java/android/service/voice/OWNERS
@@ -1,6 +1,6 @@
# Bug component: 533220
-
include /core/java/android/app/assist/OWNERS
+atneya@google.com
# The owner here should not be assist owner
adudani@google.com
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 2c585e640fdd..e8b32ce5e314 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -658,12 +658,15 @@ public class TelephonyCallback {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED = 42;
/**
* Event for listening to changes in carrier roaming non-terrestrial network eligibility.
*
- * @see CarrierRoamingNtnModeListener
+ * @see CarrierRoamingNtnModeListener#onCarrierRoamingNtnEligibleStateChanged(boolean)
*
* Device is eligible for satellite communication if all the following conditions are met:
* <ul>
@@ -679,11 +682,15 @@ public class TelephonyCallback {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43;
/**
* Event for listening to changes in carrier roaming non-terrestrial network available services
- * via callback onCarrierRoamingNtnAvailableServicesChanged().
+ * via callback {@link
+ * CarrierRoamingNtnModeListener#onCarrierRoamingNtnAvailableServicesChanged(List)}
* This callback is triggered when the available services provided by the carrier roaming
* satellite changes. The carrier roaming satellite is defined by the following conditions.
* <ul>
@@ -693,15 +700,22 @@ public class TelephonyCallback {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44;
/**
* Event for listening to carrier roaming non-terrestrial network signal strength changes.
*
- * @see CarrierRoamingNtnModeListener
+ * @see CarrierRoamingNtnModeListener#onCarrierRoamingNtnSignalStrengthChanged(
+ * NtnSignalStrength)
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45;
/**
@@ -1803,6 +1817,8 @@ public class TelephonyCallback {
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface CarrierRoamingNtnModeListener {
/**
* Callback invoked when carrier roaming non-terrestrial network mode changes.
@@ -1836,10 +1852,10 @@ public class TelephonyCallback {
* Callback invoked when carrier roaming non-terrestrial network available
* service changes.
*
- * @param availableServices The list of the supported services.
+ * @param availableServices array of supported services.
*/
default void onCarrierRoamingNtnAvailableServicesChanged(
- @NetworkRegistrationInfo.ServiceType List<Integer> availableServices) {}
+ @NonNull @NetworkRegistrationInfo.ServiceType int[] availableServices) {}
/**
* Callback invoked when carrier roaming non-terrestrial network signal strength changes.
@@ -2343,10 +2359,8 @@ public class TelephonyCallback {
(CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
if (listener == null) return;
- List<Integer> ServiceList = Arrays.stream(availableServices).boxed()
- .collect(Collectors.toList());
Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> listener.onCarrierRoamingNtnAvailableServicesChanged(ServiceList)));
+ () -> listener.onCarrierRoamingNtnAvailableServicesChanged(availableServices)));
}
public void onCarrierRoamingNtnSignalStrengthChanged(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a0feccd87a81..31330204e8c6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,7 +16,9 @@
package android.view;
+import static android.adpf.Flags.adpfViewrootimplActionDownBoost;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
@@ -1208,6 +1210,8 @@ public final class ViewRootImpl implements ViewParent,
private long mRenderThreadDrawStartTimeNs = -1;
private long mFirstFramePresentedTimeNs = -1;
+ private final boolean mSendPerfHintOnTouch;
+
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1337,6 +1341,8 @@ public final class ViewRootImpl implements ViewParent,
CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock();
mIsSubscribeGranularDisplayEventsEnabled =
com.android.server.display.feature.flags.Flags.subscribeGranularDisplayEvents();
+
+ mSendPerfHintOnTouch = adpfViewrootimplActionDownBoost();
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -2647,7 +2653,8 @@ public final class ViewRootImpl implements ViewParent,
mStopped = stopped;
final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
if (renderer != null) {
- if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+ if (DEBUG_DRAW)
+ Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
renderer.setStopped(mStopped);
}
if (!mStopped) {
@@ -7110,6 +7117,10 @@ public final class ViewRootImpl implements ViewParent,
+ "touch mode is " + mAttachInfo.mInTouchMode);
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
+ if (inTouchMode && mAttachInfo.mThreadedRenderer != null && mSendPerfHintOnTouch) {
+ mAttachInfo.mThreadedRenderer.notifyExpensiveFrame();
+ }
+
// tell the window manager
try {
IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
@@ -7968,8 +7979,9 @@ public final class ViewRootImpl implements ViewParent,
}
private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) {
- if (getConfiguration().windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_MULTI_WINDOW) {
+ final int windowingMode = getConfiguration().windowConfiguration.getWindowingMode();
+ if (!(windowingMode == WINDOWING_MODE_MULTI_WINDOW
+ || windowingMode == WINDOWING_MODE_FREEFORM)) {
return false;
}
try {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 330e46af6381..97cf8fc748e8 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -85,7 +85,7 @@ import java.util.function.IntConsumer;
* @see WindowManagerGlobal
* @hide
*/
-public final class WindowManagerImpl implements WindowManager {
+public class WindowManagerImpl implements WindowManager {
private static final String TAG = "WindowManager";
@UnsupportedAppUsage
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 905f350ca6c5..d527007e586e 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -603,7 +603,7 @@ public class AutofillFeatureFlags {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_ENABLE_RELAYOUT,
- false);
+ true);
}
/** @hide */
@@ -611,7 +611,7 @@ public class AutofillFeatureFlags {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_ENABLE_RELATIVE_LOCATION_FOR_RELAYOUT,
- false);
+ true);
}
/** @hide **/
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 52c5af8889ec..1de0474182dd 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@ import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+import static android.service.autofill.Flags.relayoutFix;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
@@ -1013,7 +1014,7 @@ public final class AutofillManager {
AutofillFeatureFlags.shouldIncludeInvisibleViewInAssistStructure();
mRelayoutFixDeprecated = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending();
- mRelayoutFix = AutofillFeatureFlags.enableRelayoutFixes();
+ mRelayoutFix = relayoutFix() && AutofillFeatureFlags.enableRelayoutFixes();
mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
}
diff --git a/core/java/android/view/flags/scroll_capture.aconfig b/core/java/android/view/flags/scroll_capture.aconfig
index fdf9c1ed8ad2..9080b1669ed5 100644
--- a/core/java/android/view/flags/scroll_capture.aconfig
+++ b/core/java/android/view/flags/scroll_capture.aconfig
@@ -3,7 +3,7 @@ container: "system"
flag {
name: "scroll_capture_target_z_order_fix"
- namespace: "system_ui"
+ namespace: "systemui"
description: "Always prefer targets with higher z-order"
bug: "365969802"
metadata {
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index b18dbbc21cda..1bc952ca6546 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
@@ -36,8 +35,6 @@ public class UserPackage {
private final UserHandle mUser;
private final PackageInfo mPackageInfo;
- public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
-
public UserPackage(@NonNull UserHandle user, @Nullable PackageInfo packageInfo) {
mUser = user;
mPackageInfo = packageInfo;
@@ -83,14 +80,6 @@ public class UserPackage {
& ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
}
- /**
- * Returns whether the package represented by {@param packageInfo} targets a sdk version
- * supported by the current framework version.
- */
- public static boolean hasCorrectTargetSdkVersion(PackageInfo packageInfo) {
- return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_SDK;
- }
-
public UserHandle getUser() {
return mUser;
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index de303f80ff9e..1a48bbb77d60 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,12 +51,6 @@ import java.lang.reflect.Method;
*/
@SystemApi
public final class WebViewFactory {
-
- // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
- /** @hide */
- private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProviderForT";
-
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
private static final String LOGTAG = "WebViewFactory";
@@ -275,8 +269,8 @@ public final class WebViewFactory {
*/
public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader)
throws ClassNotFoundException {
- return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
- true, clazzLoader);
+ return (Class<WebViewFactoryProvider>) Class.forName(
+ WebViewFactoryProvider.getWebViewFactoryClassName(), true, clazzLoader);
}
/**
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 3d6450632e06..4a2f9ba696a5 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -20,8 +20,11 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.net.Network;
import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
import java.util.List;
@@ -34,6 +37,61 @@ import java.util.List;
@SystemApi
public interface WebViewFactoryProvider {
/**
+ * Used as the requirement when Flags.useBEntryPoint() is false.
+ * @hide
+ */
+ int MINIMUM_SUPPORTED_TARGET_SDK = Build.VERSION_CODES.TIRAMISU;
+
+ /**
+ * Used as the requirement when Flags.useBEntryPoint() is true.
+ * TODO: set to the actual minimum required version code - this is just the
+ * version shipped in V.
+ * @hide
+ */
+ long MINIMUM_SUPPORTED_VERSION_CODE = 661308800L;
+
+ /**
+ * Returns whether the WebView implementation represented by {@code packageInfo}
+ * is compatible with this version of Android.
+ * @hide
+ */
+ static boolean isCompatibleImplementationPackage(@NonNull PackageInfo packageInfo) {
+ if (Flags.useBEntryPoint()) {
+ return packageInfo.versionCode >= MINIMUM_SUPPORTED_VERSION_CODE;
+ } else {
+ return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_TARGET_SDK;
+ }
+ }
+
+ /**
+ * Returns a string describing the minimum requirement for a WebView implementation
+ * to be compatible with this version of Android, for debugging purposes.
+ * @hide
+ */
+ static @NonNull String describeCompatibleImplementationPackage() {
+ if (Flags.useBEntryPoint()) {
+ return TextUtils.formatSimple("Minimum versionCode for OS support: %d",
+ MINIMUM_SUPPORTED_VERSION_CODE);
+ } else {
+ return TextUtils.formatSimple("Minimum targetSdkVersion: %d",
+ MINIMUM_SUPPORTED_TARGET_SDK);
+ }
+ }
+
+ /**
+ * Returns the name of the class that should be used when loading the
+ * WebView implementation on this version of Android.
+ * @hide
+ */
+ static @NonNull String getWebViewFactoryClassName() {
+ if (Flags.useBEntryPoint()) {
+ return "com.android.webview.chromium.WebViewChromiumFactoryProviderForB";
+ } else {
+ return "com.android.webview.chromium.WebViewChromiumFactoryProviderForT";
+ }
+ }
+
+ /**
* This Interface provides glue for implementing the backend of WebView static methods which
* cannot be implemented in-situ in the proxy class.
*/
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index c9e94d2f57f6..c5176a2f1f15 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -42,3 +42,11 @@ flag {
description: "New APIs required by File System Access"
bug: "40101963"
}
+
+flag {
+ name: "use_b_entry_point"
+ namespace: "webview"
+ description: "Use B-specific entry point to WebView APK"
+ bug: "373617389"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 7a01ad340c56..289c5cf4bf85 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -68,7 +68,8 @@ public enum DesktopModeFlags {
Flags::enableDesktopWindowingTaskbarRunningApps, true),
// TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up
ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
- ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
+ ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(
+ Flags::enableDesktopWindowingEnterTransitions, false),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
Flags::enableWindowingTransitionHandlersObservers, false),
@@ -77,7 +78,15 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS(
Flags::enableDesktopAppLaunchTransitions, false),
ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false),
- ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true);
+ ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
+ ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX(
+ Flags::enableDesktopWindowingEnterTransitionBugfix, false),
+ ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX(
+ Flags::enableDesktopWindowingExitTransitionsBugfix, false),
+ ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
+ Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, false),
+ ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
+ Flags::enableDesktopAppLaunchTransitionsBugfix, false);
private static final String TAG = "DesktopModeFlagsUtil";
// Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/KeyguardState.java b/core/java/android/window/KeyguardState.java
index 6584d30cdaed..159275abb9ef 100644
--- a/core/java/android/window/KeyguardState.java
+++ b/core/java/android/window/KeyguardState.java
@@ -30,28 +30,23 @@ import java.util.Objects;
*/
public final class KeyguardState implements Parcelable {
- private final int mDisplayId;
-
private final boolean mKeyguardShowing;
private final boolean mAodShowing;
- private KeyguardState(int displayId, boolean keyguardShowing, boolean aodShowing) {
- mDisplayId = displayId;
+ private KeyguardState(boolean keyguardShowing, boolean aodShowing) {
mKeyguardShowing = keyguardShowing;
mAodShowing = aodShowing;
}
private KeyguardState(Parcel in) {
- mDisplayId = in.readInt();
mKeyguardShowing = in.readBoolean();
mAodShowing = in.readBoolean();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mDisplayId);
dest.writeBoolean(mKeyguardShowing);
dest.writeBoolean(mAodShowing);
}
@@ -70,13 +65,6 @@ public final class KeyguardState implements Parcelable {
}
};
- /**
- * Gets the display id of this {@link KeyguardState}.
- */
- public int getDisplayId() {
- return mDisplayId;
- }
-
/** Returns the keyguard showing value. */
public boolean getKeyguardShowing() {
return mKeyguardShowing;
@@ -89,15 +77,14 @@ public final class KeyguardState implements Parcelable {
@Override
public String toString() {
- return "KeyguardState{ displayId=" + mDisplayId
- + ", keyguardShowing=" + mKeyguardShowing
+ return "KeyguardState{ keyguardShowing=" + mKeyguardShowing
+ ", aodShowing=" + mAodShowing
+ '}';
}
@Override
public int hashCode() {
- return Objects.hash(mDisplayId, mKeyguardShowing, mAodShowing);
+ return Objects.hash(mKeyguardShowing, mAodShowing);
}
@Override
@@ -105,8 +92,7 @@ public final class KeyguardState implements Parcelable {
if (!(obj instanceof KeyguardState other)) {
return false;
}
- return mDisplayId == other.mDisplayId
- && mKeyguardShowing == other.mKeyguardShowing
+ return mKeyguardShowing == other.mKeyguardShowing
&& mAodShowing == other.mAodShowing;
}
@@ -117,18 +103,11 @@ public final class KeyguardState implements Parcelable {
/** Builder to construct the {@link KeyguardState}. */
public static final class Builder {
-
- private final int mDisplayId;
-
private boolean mKeyguardShowing;
private boolean mAodShowing;
- /**
- * @param displayId the display of this {@link KeyguardState}.
- */
- public Builder(int displayId) {
- mDisplayId = displayId;
+ public Builder() {
}
/**
@@ -154,7 +133,7 @@ public final class KeyguardState implements Parcelable {
*/
@NonNull
public KeyguardState build() {
- return new KeyguardState(mDisplayId, mKeyguardShowing, mAodShowing);
+ return new KeyguardState(mKeyguardShowing, mAodShowing);
}
}
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 7ad14b0c9fe8..801698caff0e 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -112,6 +112,18 @@ flag {
}
flag {
+ name: "app_compat_async_relayout"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether we use the SurfaceViewHost overload to apply a change in /n"
+ "position and size in a Transaction provided by a callback invoked /n"
+ "after the View relayout."
+ bug: "322463856"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "app_compat_refactoring"
namespace: "large_screen_experiences_app_compat"
description: "Whether the changes about app compat refactoring are enabled./n"
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 2834e6883316..454323b60333 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -685,14 +685,6 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
}
}
- ThreadedRendererWrapper getThreadedRenderer() {
- return mRendererWrapper;
- }
-
- ViewRootWrapper getViewRoot() {
- return mViewRoot;
- }
-
private boolean shouldTriggerPerfetto(int missedFramesCount, int maxFrameTimeNanos) {
boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
&& missedFramesCount >= mTraceThresholdMissedFrames;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index ef08e49ce6d9..cbc20dcb8fde 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -31,6 +31,7 @@ import android.annotation.RequiresPermission;
import android.annotation.UiThread;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
+import android.app.Application;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
@@ -184,10 +185,12 @@ public class InteractionJankMonitor {
@GuardedBy("mLock")
private final SparseArray<RunningTracker> mRunningTrackers = new SparseArray<>();
private final Handler mWorker;
+ private final Application mCurrentApplication;
private final DisplayResolutionTracker mDisplayResolutionTracker;
private final Object mLock = new Object();
private @ColorInt int mDebugBgColor = Color.CYAN;
private double mDebugYOffset = 0.1;
+ @GuardedBy("mLock")
private InteractionMonitorDebugOverlay mDebugOverlay;
private volatile boolean mEnabled = DEFAULT_ENABLED;
@@ -216,13 +219,15 @@ public class InteractionJankMonitor {
mWorker = worker.getThreadHandler();
mDisplayResolutionTracker = new DisplayResolutionTracker(mWorker);
- final Context context = ActivityThread.currentApplication();
- if (context == null || context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
+ mCurrentApplication = ActivityThread.currentApplication();
+ if (mCurrentApplication == null || mCurrentApplication.checkCallingOrSelfPermission(
+ READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
Log.w(TAG, "Initializing without READ_DEVICE_CONFIG permission."
+ " enabled=" + mEnabled + ", interval=" + mSamplingInterval
+ ", missedFrameThreshold=" + mTraceThresholdMissedFrames
+ ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis
- + ", package=" + (context == null ? "null" : context.getPackageName()));
+ + ", package=" + (mCurrentApplication == null ? "null"
+ : mCurrentApplication.getPackageName()));
return;
}
@@ -234,8 +239,8 @@ public class InteractionJankMonitor {
new HandlerExecutor(mWorker), this::updateProperties);
} catch (SecurityException ex) {
Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
- + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
- + ", package=" + context.getPackageName());
+ + mCurrentApplication.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
+ + ", package=" + mCurrentApplication.getPackageName());
}
});
}
@@ -405,7 +410,11 @@ public class InteractionJankMonitor {
RunningTracker tracker = putTrackerIfNoCurrent(cujType, () ->
new RunningTracker(
- conf, createFrameTracker(conf), () -> cancel(cujType, REASON_CANCEL_TIMEOUT)));
+ conf, createFrameTracker(conf), () -> {
+ Log.w(TAG, "CUJ cancelled due to timeout, CUJ="
+ + Cuj.getNameOfCuj(cujType));
+ cancel(cujType, REASON_CANCEL_TIMEOUT);
+ }));
if (tracker == null) {
return false;
}
@@ -534,7 +543,7 @@ public class InteractionJankMonitor {
mRunningTrackers.put(cuj, tracker);
if (mDebugOverlay != null) {
- mDebugOverlay.onTrackerAdded(cuj, tracker);
+ mDebugOverlay.onTrackerAdded(cuj);
}
return tracker;
@@ -569,7 +578,7 @@ public class InteractionJankMonitor {
running.mConfig.getHandler().removeCallbacks(running.mTimeoutAction);
mRunningTrackers.remove(cuj);
if (mDebugOverlay != null) {
- mDebugOverlay.onTrackerRemoved(cuj, reason, mRunningTrackers);
+ mDebugOverlay.onTrackerRemoved(cuj, reason);
}
return false;
}
@@ -592,14 +601,18 @@ public class InteractionJankMonitor {
mEnabled = properties.getBoolean(property, DEFAULT_ENABLED);
case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY -> {
// Never allow the debug overlay to be used on user builds
- boolean debugOverlayEnabled = Build.IS_DEBUGGABLE
- && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED);
- if (debugOverlayEnabled && mDebugOverlay == null) {
- mDebugOverlay = new InteractionMonitorDebugOverlay(
- mLock, mDebugBgColor, mDebugYOffset);
- } else if (!debugOverlayEnabled && mDebugOverlay != null) {
- mDebugOverlay.dispose();
- mDebugOverlay = null;
+ if (Build.IS_USER) break;
+ boolean debugOverlayEnabled = properties.getBoolean(property,
+ DEFAULT_DEBUG_OVERLAY_ENABLED);
+ synchronized (mLock) {
+ if (debugOverlayEnabled && mDebugOverlay == null) {
+ // Use the worker thread as the UI thread for the debug overlay:
+ mDebugOverlay = new InteractionMonitorDebugOverlay(
+ mCurrentApplication, mWorker, mDebugBgColor, mDebugYOffset);
+ } else if (!debugOverlayEnabled && mDebugOverlay != null) {
+ mDebugOverlay.dispose();
+ mDebugOverlay = null;
+ }
}
}
default -> Log.w(TAG, "Got a change event for an unknown property: "
diff --git a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
index d9cac12c3372..009837b686c3 100644
--- a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
+++ b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
@@ -16,24 +16,36 @@
package com.android.internal.jank;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Gravity.CENTER;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+import android.annotation.AnyThread;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.annotation.UiThread;
-import android.app.ActivityThread;
+import android.app.Application;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.RecordingCanvas;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.Trace;
+import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.view.WindowCallbacks;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.jank.FrameTracker.Reasons;
/**
@@ -50,210 +62,171 @@ import com.android.internal.jank.FrameTracker.Reasons;
* <li> Grey text indicates the CUJ ended normally and is no longer running
* <li> Red text with a strikethrough indicates the CUJ was canceled or ended abnormally
* </ul>
+ *
* @hide
*/
-class InteractionMonitorDebugOverlay implements WindowCallbacks {
+class InteractionMonitorDebugOverlay {
private static final String TAG = "InteractionMonitorDebug";
private static final int REASON_STILL_RUNNING = -1000;
- private final Object mLock;
// Sparse array where the key in the CUJ and the value is the session status, or null if
// it's currently running
- @GuardedBy("mLock")
+ private final Application mCurrentApplication;
+ private final Handler mUiThread;
+ private final DebugOverlayView mDebugOverlayView;
+ private final WindowManager mWindowManager;
private final SparseIntArray mRunningCujs = new SparseIntArray();
- private Handler mHandler = null;
- private FrameTracker.ViewRootWrapper mViewRoot = null;
- private final Paint mDebugPaint;
- private final Paint.FontMetrics mDebugFontMetrics;
- // Used to display the overlay in a different color and position for different processes.
- // Otherwise, two overlays will overlap and be difficult to read.
- private final int mBgColor;
- private final double mYOffset;
- private final String mPackageName;
- private static final String TRACK_NAME = "InteractionJankMonitor";
- InteractionMonitorDebugOverlay(Object lock, @ColorInt int bgColor, double yOffset) {
- mLock = lock;
- mBgColor = bgColor;
- mYOffset = yOffset;
- mDebugPaint = new Paint();
- mDebugPaint.setAntiAlias(false);
- mDebugFontMetrics = new Paint.FontMetrics();
- final Context context = ActivityThread.currentApplication();
- mPackageName = context == null ? "null" : context.getPackageName();
- }
+ InteractionMonitorDebugOverlay(@NonNull Application currentApplication,
+ @NonNull @UiThread Handler uiThread, @ColorInt int bgColor, double yOffset) {
+ mCurrentApplication = currentApplication;
+ mUiThread = uiThread;
+ final Display display = mCurrentApplication.getSystemService(
+ DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+ final Context windowContext = mCurrentApplication.createDisplayContext(
+ display).createWindowContext(TYPE_SYSTEM_OVERLAY, null /* options */);
+ mWindowManager = windowContext.getSystemService(WindowManager.class);
- @UiThread
- void dispose() {
- if (mViewRoot != null && mHandler != null) {
- mHandler.runWithScissors(() -> mViewRoot.removeWindowCallbacks(this),
- InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
- forceRedraw();
- }
- mHandler = null;
- mViewRoot = null;
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0);
- }
+ final Rect size = mWindowManager.getCurrentWindowMetrics().getBounds();
- @UiThread
- private boolean attachViewRootIfNeeded(InteractionJankMonitor.RunningTracker tracker) {
- FrameTracker.ViewRootWrapper viewRoot = tracker.mTracker.getViewRoot();
- if (mViewRoot == null && viewRoot != null) {
- // Add a trace marker so we can identify traces that were captured while the debug
- // overlay was enabled. Traces that use the debug overlay should NOT be used for
- // performance analysis.
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0);
- mHandler = tracker.mConfig.getHandler();
- mViewRoot = viewRoot;
- mHandler.runWithScissors(() -> viewRoot.addWindowCallbacks(this),
- InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
- forceRedraw();
- return true;
- }
- return false;
- }
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
- @GuardedBy("mLock")
- private float getWidthOfLongestCujName(int cujFontSize) {
- mDebugPaint.setTextSize(cujFontSize);
- float maxLength = 0;
- for (int i = 0; i < mRunningCujs.size(); i++) {
- String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
- float textLength = mDebugPaint.measureText(cujName);
- if (textLength > maxLength) {
- maxLength = textLength;
- }
- }
- return maxLength;
- }
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
- private float getTextHeight(int textSize) {
- mDebugPaint.setTextSize(textSize);
- mDebugPaint.getFontMetrics(mDebugFontMetrics);
- return mDebugFontMetrics.descent - mDebugFontMetrics.ascent;
- }
+ lp.width = size.width();
+ lp.height = size.height();
+ lp.gravity = CENTER;
+ lp.setTitle("InteractionMonitorDebugOverlay");
- private int dipToPx(int dip) {
- if (mViewRoot != null) {
- return mViewRoot.dipToPx(dip);
- } else {
- return dip;
+ if (!mUiThread.getLooper().isCurrentThread()) {
+ Log.e(TAG, "InteractionMonitorDebugOverlay must be constructed on "
+ + "InteractionJankMonitor's worker thread");
}
+ mDebugOverlayView = new DebugOverlayView(mCurrentApplication, bgColor, yOffset);
+ mWindowManager.addView(mDebugOverlayView, lp);
}
- @UiThread
- private void forceRedraw() {
- if (mViewRoot != null && mHandler != null) {
- mHandler.runWithScissors(() -> {
- mViewRoot.requestInvalidateRootRenderNode();
- mViewRoot.getView().invalidate();
- }, InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
- }
+ @AnyThread
+ void onTrackerAdded(@Cuj.CujType int addedCuj) {
+ mUiThread.post(() -> {
+ String cujName = Cuj.getNameOfCuj(addedCuj);
+ Log.i(TAG, cujName + " started");
+ // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
+ // is still running
+ mRunningCujs.put(addedCuj, REASON_STILL_RUNNING);
+ mDebugOverlayView.setVisibility(VISIBLE);
+ mDebugOverlayView.invalidate();
+ });
}
- @UiThread
- void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason,
- SparseArray<InteractionJankMonitor.RunningTracker> runningTrackers) {
- synchronized (mLock) {
+ @AnyThread
+ void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason) {
+ mUiThread.post(() -> {
mRunningCujs.put(removedCuj, reason);
- boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
- if (isLoggable) {
- String cujName = Cuj.getNameOfCuj(removedCuj);
- Log.d(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled"));
- }
+ String cujName = Cuj.getNameOfCuj(removedCuj);
+ Log.i(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled"));
// If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) {
- if (isLoggable) Log.d(TAG, "All CUJs ended");
+ Log.i(TAG, "All CUJs ended");
mRunningCujs.clear();
- dispose();
- } else {
- boolean needsNewViewRoot = true;
- if (mViewRoot != null) {
- // Check to see if this viewroot is still associated with one of the running
- // trackers
- for (int i = 0; i < runningTrackers.size(); i++) {
- if (mViewRoot.equals(
- runningTrackers.valueAt(i).mTracker.getViewRoot())) {
- needsNewViewRoot = false;
- break;
- }
- }
- }
- if (needsNewViewRoot) {
- dispose();
- for (int i = 0; i < runningTrackers.size(); i++) {
- if (attachViewRootIfNeeded(runningTrackers.valueAt(i))) {
- break;
- }
- }
- } else {
- forceRedraw();
- }
+ mDebugOverlayView.setVisibility(INVISIBLE);
}
- }
+ mDebugOverlayView.invalidate();
+ });
}
- @UiThread
- void onTrackerAdded(@Cuj.CujType int addedCuj, InteractionJankMonitor.RunningTracker tracker) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- String cujName = Cuj.getNameOfCuj(addedCuj);
- Log.d(TAG, cujName + " started");
- }
- synchronized (mLock) {
- // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
- // is still running
- mRunningCujs.put(addedCuj, REASON_STILL_RUNNING);
- attachViewRootIfNeeded(tracker);
- forceRedraw();
- }
+ @AnyThread
+ void dispose() {
+ mUiThread.post(() -> {
+ mWindowManager.removeView(mDebugOverlayView);
+ });
}
- @Override
- public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen,
- Rect systemInsets, Rect stableInsets) {
- }
+ @UiThread
+ private class DebugOverlayView extends View {
+ private static final String TRACK_NAME = "InteractionJankMonitor";
- @Override
- public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen,
- Rect systemInsets, Rect stableInsets) {
- }
+ // Used to display the overlay in a different color and position for different processes.
+ // Otherwise, two overlays will overlap and be difficult to read.
+ private final int mBgColor;
+ private final double mYOffset;
- @Override
- public void onWindowDragResizeEnd() {
- }
+ private final float mDensity;
+ private final Paint mDebugPaint;
+ private final Paint.FontMetrics mDebugFontMetrics;
+ private final String mPackageName;
- @Override
- public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
- return false;
- }
+ private DebugOverlayView(Context context, @ColorInt int bgColor, double yOffset) {
+ super(context);
+ setVisibility(INVISIBLE);
+ mBgColor = bgColor;
+ mYOffset = yOffset;
+ final DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+ mDensity = displayMetrics.density;
+ mDebugPaint = new Paint();
+ mDebugPaint.setAntiAlias(false);
+ mDebugFontMetrics = new Paint.FontMetrics();
+ mPackageName = mCurrentApplication.getPackageName();
+ }
- @Override
- public void onRequestDraw(boolean reportNextDraw) {
- }
+ private int dipToPx(int dip) {
+ return (int) (mDensity * dip + 0.5f);
+ }
+
+ private float getTextHeight(int textSize) {
+ mDebugPaint.setTextSize(textSize);
+ mDebugPaint.getFontMetrics(mDebugFontMetrics);
+ return mDebugFontMetrics.descent - mDebugFontMetrics.ascent;
+ }
- @Override
- public void onPostDraw(RecordingCanvas canvas) {
- final int padding = dipToPx(5);
- final int h = canvas.getHeight();
- final int w = canvas.getWidth();
- // Draw sysui CUjs near the bottom of the screen so they don't overlap with the shade,
- // and draw launcher CUJs near the top of the screen so they don't overlap with gestures
- final int dy = (int) (h * mYOffset);
- int packageNameFontSize = dipToPx(12);
- int cujFontSize = dipToPx(18);
- final float cujNameTextHeight = getTextHeight(cujFontSize);
- final float packageNameTextHeight = getTextHeight(packageNameFontSize);
+ private float getWidthOfLongestCujName(int cujFontSize) {
+ mDebugPaint.setTextSize(cujFontSize);
+ float maxLength = 0;
+ for (int i = 0; i < mRunningCujs.size(); i++) {
+ String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
+ float textLength = mDebugPaint.measureText(cujName);
+ if (textLength > maxLength) {
+ maxLength = textLength;
+ }
+ }
+ return maxLength;
+ }
+
+ @Override
+ protected void onDraw(@NonNull Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Add a trace marker so we can identify traces that were captured while the debug
+ // overlay was enabled. Traces that use the debug overlay should NOT be used for
+ // performance analysis.
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0);
- synchronized (mLock) {
+ final int padding = dipToPx(5);
+ final int h = getHeight();
+ final int w = getWidth();
+ final int dy = (int) (h * mYOffset);
+ int packageNameFontSize = dipToPx(12);
+ int cujFontSize = dipToPx(18);
+ final float cujNameTextHeight = getTextHeight(cujFontSize);
+ final float packageNameTextHeight = getTextHeight(packageNameFontSize);
float maxLength = getWidthOfLongestCujName(cujFontSize);
final int dx = (int) ((w - maxLength) / 2f);
canvas.translate(dx, dy);
// Draw background rectangle for displaying the text showing the CUJ name
mDebugPaint.setColor(mBgColor);
- canvas.drawRect(
- -padding * 2, // more padding on top so we can draw the package name
- -padding,
- padding * 2 + maxLength,
+ canvas.drawRect(-padding * 2, // more padding on top so we can draw the package name
+ -padding, padding * 2 + maxLength,
padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(),
mDebugPaint);
mDebugPaint.setTextSize(packageNameFontSize);
@@ -280,6 +253,7 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
canvas.translate(0, cujNameTextHeight);
canvas.drawText(cujName, 0, 0, mDebugPaint);
}
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0);
}
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e2f3d2a32d0b..7d77ff054fe6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8696,7 +8696,7 @@
android:featureFlag="android.security.secure_lockdown" />
<!-- Allows app to enter trade-in-mode.
- <p>Protection level: signature|privileged
+ <p>Protection level: signature
@hide
-->
<permission android:name="android.permission.ENTER_TRADE_IN_MODE"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0d13ca83ec59..f199159039d6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2174,6 +2174,16 @@
<item>com.android.location.fused</item>
</string-array>
+ <!-- Package name providing population density location support. -->
+ <string name="config_populationDensityProviderPackageName" translatable="false">com.android.location.populationdensity</string>
+
+ <!-- Whether to enable population density provider overlay, which allows the population density provider to
+ be replaced by an app at run-time. When disabled, only the
+ config_populationDensityProviderPackageName package will be searched for a population density
+ provider, otherwise any system package is eligible. Anyone who wants to disable the overlay
+ mechanism can set it to false. -->
+ <bool name="config_enablePopulationDensityProviderOverlay" translatable="false">true</bool>
+
<!-- Package name of the extension software fallback. -->
<string name="config_extensionFallbackPackageName" translatable="false"></string>
@@ -2451,6 +2461,9 @@
<string name="config_systemCallStreaming" translatable="false"></string>
<!-- The name of the package that will hold the default retail demo role. -->
<string name="config_defaultRetailDemo" translatable="false"></string>
+ <!-- The name of the package that will hold the default reserved for testing profile group
+ exclusivity role. -->
+ <string name="config_defaultReservedForTestingProfileGroupExclusivity" translatable="false">android.app.rolemultiuser.cts.app</string>
<!-- The component name of the wear service class that will be started by the system server. -->
<string name="config_wearServiceComponent" translatable="false"></string>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index e8063a27d77b..bb76b9fae8d7 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -488,8 +488,12 @@
<java-symbol type="string" name="config_satellite_carrier_roaming_non_emergency_session_class" />
<!-- Whether to show the system notification to users whenever there is a change
- in the satellite availability state at the current location. -->
+ in the satellite availability state at the current location. -->
<bool name="config_satellite_should_notify_availability">true</bool>
<java-symbol type="bool" name="config_satellite_should_notify_availability" />
+ <!-- Whether to allow check message datagrams to be sent even when the satellite modem is in
+ not connected state. -->
+ <bool name="config_satellite_allow_check_message_in_not_connected">false</bool>
+ <java-symbol type="bool" name="config_satellite_allow_check_message_in_not_connected" />
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 76ff56559adf..070112853afe 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -151,6 +151,9 @@
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
@hide @SystemApi -->
<public name="config_systemDependencyInstaller" />
+ <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_PLATFORM_API_ENABLED)
+ @hide @SystemApi -->
+ <public name="config_defaultReservedForTestingProfileGroupExclusivity" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01b30000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 380b2971b783..a9ade634a248 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2012,6 +2012,8 @@
<java-symbol type="array" name="config_locationProviderPackageNames" />
<java-symbol type="array" name="config_locationDriverAssistancePackageNames" />
<java-symbol type="array" name="config_locationExtraPackageNames" />
+ <java-symbol type="string" name="config_populationDensityProviderPackageName" />
+ <java-symbol type="bool" name="config_enablePopulationDensityProviderOverlay" />
<java-symbol type="array" name="config_testLocationProviders" />
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
<java-symbol type="array" name="config_defaultNotificationVibeWaveform" />
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
index 5a2a723cacee..f9f43bc8dfae 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -85,6 +85,9 @@ public class SplitScreenConstants {
public @interface SplitIndex {
}
+ /** Signifies that user is currently not in split screen. */
+ public static final int NOT_IN_SPLIT = -1;
+
/**
* A snap target for two apps, where the split is 33-66. With FLAG_ENABLE_FLEXIBLE_SPLIT,
* only used on tablets.
@@ -152,6 +155,23 @@ public class SplitScreenConstants {
public @interface PersistentSnapPosition {}
/**
+ * These are all the valid "states" that split screen can be in. It's the set of
+ * {@link PersistentSnapPosition} + {@link #NOT_IN_SPLIT}.
+ */
+ @IntDef(value = {
+ NOT_IN_SPLIT,
+ SNAP_TO_2_33_66,
+ SNAP_TO_2_50_50,
+ SNAP_TO_2_66_33,
+ SNAP_TO_2_90_10,
+ SNAP_TO_2_10_90,
+ SNAP_TO_3_33_33_33,
+ SNAP_TO_3_45_45_10,
+ SNAP_TO_3_10_45_45,
+ })
+ public @interface SplitScreenState {}
+
+ /**
* Checks if the snapPosition in question is a {@link PersistentSnapPosition}.
*/
public static boolean isPersistentSnapPosition(@SnapPosition int snapPosition) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 12d20bf0e517..f532be6b8277 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -96,14 +96,6 @@ public class DisplayController {
}
/**
- * Get all the displays from DisplayManager.
- */
- public Display[] getDisplays() {
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- return displayManager.getDisplays();
- }
-
- /**
* Gets the DisplayLayout associated with a display.
*/
public @Nullable DisplayLayout getDisplayLayout(int displayId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 6beff1979e6d..1852cda7e804 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -55,6 +55,7 @@ import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsController;
import android.view.InsetsSource;
@@ -142,6 +143,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@ShellMainThread
private final Handler mHandler;
+ /** Singleton source of truth for the current state of split screen on this device. */
+ private final SplitState mSplitState;
+
private int mDividerWindowWidth;
private int mDividerInsets;
private int mDividerSize;
@@ -204,7 +208,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayController displayController, DisplayImeController displayImeController,
- ShellTaskOrganizer taskOrganizer, int parallaxType, @ShellMainThread Handler handler) {
+ ShellTaskOrganizer taskOrganizer, int parallaxType, SplitState splitState,
+ @ShellMainThread Handler handler) {
mHandler = handler;
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
@@ -220,6 +225,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
+ mSplitState = splitState;
final Resources res = mContext.getResources();
mDimNonImeSide = res.getBoolean(R.bool.config_dimNonImeAttachedSide);
@@ -381,6 +387,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return mDividerSnapAlgorithm.calculateNearestSnapPosition(mDividerPosition);
}
+ /** Updates the {@link SplitState} using the current divider position. */
+ public void updateStateWithCurrentPosition() {
+ mSplitState.set(calculateCurrentSnapPosition());
+ }
+
/**
* Returns the divider position as a fraction from 0 to 1.
*/
@@ -413,7 +424,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
removeTouchZones();
}
- int currentPosition = calculateCurrentSnapPosition();
+ int currentPosition = mSplitState.get();
+ // TODO (b/349828130): Can delete this warning after brief soak time.
+ if (currentPosition != calculateCurrentSnapPosition()) {
+ Log.wtf(TAG, "SplitState is " + mSplitState.get()
+ + ", expected " + calculateCurrentSnapPosition());
+ }
+
switch (currentPosition) {
case SNAP_TO_2_10_90:
case SNAP_TO_3_10_45_45:
@@ -764,7 +781,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
break;
default:
flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator,
- () -> setDividerPosition(snapTarget.position, true /* applyLayoutChange */));
+ () -> {
+ setDividerPosition(snapTarget.position, true /* applyLayoutChange */);
+ mSplitState.set(snapTarget.snapPosition);
+ });
break;
}
}
@@ -836,10 +856,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
/** Fling divider from current position to center position. */
public void flingDividerToCenter(@Nullable Runnable finishCallback) {
- final int pos = mDividerSnapAlgorithm.getMiddleTarget().position;
+ final SnapTarget target = mDividerSnapAlgorithm.getMiddleTarget();
+ final int pos = target.position;
flingDividerPosition(getDividerPosition(), pos, FLING_ENTER_DURATION, FAST_OUT_SLOW_IN,
() -> {
setDividerPosition(pos, true /* applyLayoutChange */);
+ mSplitState.set(target.snapPosition);
if (finishCallback != null) {
finishCallback.run();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
new file mode 100644
index 000000000000..71758e0d2159
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split;
+
+import static com.android.wm.shell.shared.split.SplitScreenConstants.NOT_IN_SPLIT;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SplitScreenState;
+
+/**
+ * A class that manages the "state" of split screen. See {@link SplitScreenState} for definitions.
+ */
+public class SplitState {
+ private @SplitScreenState int mState = NOT_IN_SPLIT;
+
+ /** Updates the current state of split screen on this device. */
+ public void set(@SplitScreenState int newState) {
+ mState = newState;
+ }
+
+ /** Reports the current state of split screen on this device. */
+ public @SplitScreenState int get() {
+ return mState;
+ }
+
+ /** Sets NOT_IN_SPLIT when user exits split. */
+ public void exit() {
+ set(NOT_IN_SPLIT);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
index 523e2f5cf7dc..2c52e9ea3fc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
@@ -58,7 +58,8 @@ interface LetterboxController {
fun updateLetterboxSurfaceBounds(
key: LetterboxKey,
transaction: Transaction,
- taskBounds: Rect
+ taskBounds: Rect,
+ activityBounds: Rect
)
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
index adb034cc4787..771d61832889 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
@@ -16,5 +16,18 @@
package com.android.wm.shell.compatui.letterbox
+import android.view.SurfaceControl
+
// The key to use for identify the letterbox sessions.
-data class LetterboxKey(val displayId: Int, val taskId: Int) \ No newline at end of file
+data class LetterboxKey(val displayId: Int, val taskId: Int)
+
+// Encapsulates the surfaces in the multiple surfaces scenario.
+data class LetterboxSurfaces(
+ var leftSurface: SurfaceControl? = null,
+ var topSurface: SurfaceControl? = null,
+ var rightSurface: SurfaceControl? = null,
+ var bottomSurface: SurfaceControl? = null
+) : Iterable<SurfaceControl?> {
+ override fun iterator() =
+ listOf(leftSurface, topSurface, rightSurface, bottomSurface).iterator()
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
index 8b830e769c70..a48dc8be0d1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
@@ -41,6 +41,8 @@ class LetterboxTransitionObserver(
companion object {
@JvmStatic
private val TAG = "LetterboxTransitionObserver"
+ @JvmStatic
+ private val EMPTY_BOUNDS = Rect()
}
init {
@@ -86,10 +88,13 @@ class LetterboxTransitionObserver(
startTransaction,
change.leash
)
+ val activityBounds =
+ ti.appCompatTaskInfo.topActivityLetterboxBounds ?: EMPTY_BOUNDS
updateLetterboxSurfaceBounds(
key,
startTransaction,
- taskBounds
+ taskBounds,
+ activityBounds
)
}
updateLetterboxSurfaceVisibility(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
new file mode 100644
index 000000000000..5129d03b9dbc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.graphics.Rect
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.dagger.WMSingleton
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
+import javax.inject.Inject
+
+/**
+ * Component responsible for handling the lifecycle of multiple letterbox surfaces when needed.
+ */
+@WMSingleton
+class MultiSurfaceLetterboxController @Inject constructor(
+ private val letterboxBuilder: LetterboxSurfaceBuilder
+) : LetterboxController {
+
+ companion object {
+ @JvmStatic
+ private val TAG = "MultiSurfaceLetterboxController"
+ }
+
+ private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxSurfaces>()
+
+ override fun createLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction,
+ parentLeash: SurfaceControl
+ ) {
+ val surfaceBuilderFn = { position: String ->
+ letterboxBuilder.createSurface(
+ transaction,
+ parentLeash,
+ "ShellLetterboxSurface-$key-$position",
+ "MultiSurfaceLetterboxController#createLetterboxSurface"
+ )
+ }
+ letterboxMap.runOnItem(key, onMissed = { k, m ->
+ m[k] = LetterboxSurfaces(
+ leftSurface = surfaceBuilderFn("Left"),
+ topSurface = surfaceBuilderFn("Top"),
+ rightSurface = surfaceBuilderFn("Right"),
+ bottomSurface = surfaceBuilderFn("Bottom"),
+ )
+ })
+ }
+
+ override fun destroyLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.forEach { s ->
+ s.remove(transaction)
+ }
+ })
+ letterboxMap.remove(key)
+ }
+
+ override fun updateLetterboxSurfaceVisibility(
+ key: LetterboxKey,
+ transaction: Transaction,
+ visible: Boolean
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.forEach { s ->
+ s.setVisibility(transaction, visible)
+ }
+ })
+ }
+
+ override fun updateLetterboxSurfaceBounds(
+ key: LetterboxKey,
+ transaction: Transaction,
+ taskBounds: Rect,
+ activityBounds: Rect
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.updateSurfacesBounds(transaction, taskBounds, activityBounds)
+ })
+ }
+
+ override fun dump() {
+ ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
+ }
+
+ /*
+ * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present.
+ */
+ private fun MutableMap<LetterboxKey, LetterboxSurfaces>.runOnItem(
+ key: LetterboxKey,
+ onFound: (LetterboxSurfaces) -> Unit = { _ -> },
+ onMissed: (
+ LetterboxKey,
+ MutableMap<LetterboxKey, LetterboxSurfaces>
+ ) -> Unit = { _, _ -> }
+ ) {
+ this[key]?.let {
+ return onFound(it)
+ }
+ return onMissed(key, this)
+ }
+
+ private fun SurfaceControl?.remove(
+ tx: Transaction
+ ) = this?.let {
+ tx.remove(this)
+ }
+
+ private fun SurfaceControl?.setVisibility(
+ tx: Transaction,
+ visible: Boolean
+ ) = this?.let {
+ tx.setVisibility(this, visible)
+ }
+
+ private fun Transaction.moveAndCrop(
+ surface: SurfaceControl,
+ rect: Rect
+ ): Transaction =
+ setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
+ .setWindowCrop(
+ surface,
+ rect.width(),
+ rect.height()
+ )
+
+ private fun LetterboxSurfaces.updateSurfacesBounds(
+ tx: Transaction,
+ taskBounds: Rect,
+ activityBounds: Rect
+ ) {
+ // Update the bounds depending on the activity position.
+ leftSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(taskBounds.left, taskBounds.top, activityBounds.left, taskBounds.bottom)
+ )
+ }
+ rightSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(activityBounds.right, taskBounds.top, taskBounds.right, taskBounds.bottom)
+ )
+ }
+ topSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(taskBounds.left, taskBounds.top, taskBounds.right, activityBounds.top)
+ )
+ }
+ bottomSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(taskBounds.left, activityBounds.bottom, taskBounds.right, taskBounds.bottom)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
index f21a7272287e..a67f6082c892 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
@@ -34,7 +34,7 @@ class SingleSurfaceLetterboxController @Inject constructor(
companion object {
@JvmStatic
- private val TAG = "LetterboxController"
+ private val TAG = "SingleSurfaceLetterboxController"
}
private val letterboxMap = mutableMapOf<LetterboxKey, SurfaceControl>()
@@ -93,7 +93,8 @@ class SingleSurfaceLetterboxController @Inject constructor(
override fun updateLetterboxSurfaceBounds(
key: LetterboxKey,
transaction: Transaction,
- taskBounds: Rect
+ taskBounds: Rect,
+ activityBounds: Rect
) {
letterboxMap.runOnItem(key, onFound = { item ->
item.run {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 33e4fd8c1a46..aebd94fc173a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -30,6 +30,7 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.dagger.pip.TvPipModule;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
@@ -89,6 +90,7 @@ public class TvWMShellModule {
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
@ShellMainThread ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
@@ -96,6 +98,6 @@ public class TvWMShellModule {
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
- mainExecutor, mainHandler, systemWindows);
+ splitState, mainExecutor, mainHandler, systemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 47084e17d029..de86b22e0a99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -72,6 +72,7 @@ import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
@@ -862,6 +863,12 @@ public abstract class WMShellBaseModule {
return Optional.empty();
}
+ @WMSingleton
+ @Provides
+ static SplitState provideSplitState() {
+ return new SplitState();
+ }
+
//
// Starting window
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 0bf3d9b4515f..df2b849019a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.dagger;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
import static android.window.DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS;
@@ -50,6 +51,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.apptoweb.AssistContentRequester;
+import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -68,6 +70,7 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.transition.TransitionStateHolder;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxController;
@@ -87,7 +90,6 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
-import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -503,6 +505,7 @@ public abstract class WMShellModule {
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new SplitScreenController(
@@ -526,6 +529,7 @@ public abstract class WMShellModule {
desktopTasksController,
null /* stageCoordinator */,
multiInstanceHelper,
+ splitState,
mainExecutor,
mainHandler);
}
@@ -851,7 +855,8 @@ public abstract class WMShellModule {
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor) {
return (Flags.enableDesktopWindowingTransitions()
- || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
+ || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue()
+ || ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX.isTrue())
? new SpringDragToDesktopTransitionHandler(
context, transitions, rootTaskDisplayAreaOrganizer, interactionJankMonitor)
: new DefaultDragToDesktopTransitionHandler(
@@ -1049,6 +1054,7 @@ public abstract class WMShellModule {
Transitions transitions,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
+ Optional<BackAnimationController> backAnimationController,
ShellInit shellInit) {
return desktopUserRepositories.flatMap(
repository ->
@@ -1059,6 +1065,7 @@ public abstract class WMShellModule {
transitions,
shellTaskOrganizer,
desktopMixedTransitionHandler.get(),
+ backAnimationController.get(),
shellInit)));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 33d94d5bb474..36904fb5a05f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -78,7 +78,10 @@ class DesktopMixedTransitionHandler(
/** Starts close transition and handles or delegates desktop task close animation. */
override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue) {
+ if (
+ !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue &&
+ !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue
+ ) {
return freeformTaskTransitionHandler.startRemoveTransition(wct)
}
requireNotNull(wct)
@@ -102,7 +105,8 @@ class DesktopMixedTransitionHandler(
): IBinder {
if (
!Flags.enableFullyImmersiveInDesktop() &&
- !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue &&
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
) {
return transitions.startTransition(transitionType, wct, /* handler= */ null)
}
@@ -250,7 +254,10 @@ class DesktopMixedTransitionHandler(
minimizeChange?.taskInfo?.taskId,
immersiveExitChange?.taskInfo?.taskId,
)
- if (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+ if (
+ DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue ||
+ DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
+ ) {
// Only apply minimize change reparenting here if we implement the new app launch
// transitions, otherwise this reparenting is handled in the default handler.
minimizeChange?.let {
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 cf8a9ae27f0b..45425e27a078 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
@@ -136,6 +136,7 @@ import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel
import java.io.PrintWriter
import java.util.Optional
import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
import java.util.function.Consumer
/** Handles moving tasks in and out of desktop */
@@ -465,12 +466,15 @@ class DesktopTasksController(
taskSurface: SurfaceControl,
) {
logV("startDragToDesktop taskId=%d", taskInfo.taskId)
- interactionJankMonitor.begin(
- taskSurface,
- context,
- handler,
- CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD,
- )
+ val jankConfigBuilder =
+ InteractionJankMonitor.Configuration.Builder.withSurface(
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD,
+ context,
+ taskSurface,
+ handler,
+ )
+ .setTimeout(APP_HANDLE_DRAG_HOLD_CUJ_TIMEOUT_MS)
+ interactionJankMonitor.begin(jankConfigBuilder)
dragToDesktopTransitionHandler.startDragToDesktopTransition(
taskInfo.taskId,
dragToDesktopValueAnimator,
@@ -1777,16 +1781,6 @@ class DesktopTasksController(
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
taskRepository.addClosingTask(task.displayId, task.taskId)
desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
- } else if (requestType == TRANSIT_CLOSE) {
- // Handle closing tasks, tasks that are going to back are handled in
- // [DesktopTasksTransitionObserver].
- desktopMixedTransitionHandler.addPendingMixedTransition(
- DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
- transition,
- task.taskId,
- taskRepository.getVisibleTaskCount(task.displayId) == 1,
- )
- )
}
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
@@ -1928,7 +1922,10 @@ class DesktopTasksController(
launchTaskId: Int,
minimizeTaskId: Int?,
) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+ if (
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue &&
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
+ ) {
return
}
// TODO b/359523924: pass immersive task here?
@@ -2645,6 +2642,10 @@ class DesktopTasksController(
val DESKTOP_MODE_INITIAL_BOUNDS_SCALE =
SystemProperties.getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
+ // Timeout used for CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD, this is longer than the
+ // default timeout to avoid timing out in the middle of a drag action.
+ private val APP_HANDLE_DRAG_HOLD_CUJ_TIMEOUT_MS: Long = TimeUnit.SECONDS.toMillis(10L)
+
private const val TAG = "DesktopTasksController"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index b1667c52737c..9625b71ad3cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -30,6 +30,7 @@ import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
@@ -48,6 +49,7 @@ class DesktopTasksTransitionObserver(
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
+ private val backAnimationController: BackAnimationController,
shellInit: ShellInit,
) : Transitions.TransitionObserver {
@@ -128,21 +130,80 @@ class DesktopTasksTransitionObserver(
)
}
}
+ } else if (info.type == TRANSIT_CLOSE) {
+ // In some cases app will be closing as a result of back navigation but we would like
+ // to minimize. Mark the task closing as minimized.
+ var hasWallpaperClosing = false
+ var minimizingTask: Int? = null
+ for (change in info.changes) {
+ val taskInfo = change.taskInfo
+ if (taskInfo == null || taskInfo.taskId == -1) continue
+ if (change.mode != TRANSIT_CLOSE) continue
+
+ if (minimizingTask == null) {
+ minimizingTask = getMinimizingTaskForClosingTransition(taskInfo)
+ }
+
+ if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
+ hasWallpaperClosing = true
+ }
+ }
+
+ if (minimizingTask == null) return
+ // If the transition has wallpaper closing, it means we are moving out of desktop.
+ desktopMixedTransitionHandler.addPendingMixedTransition(
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition,
+ minimizingTask,
+ isLastTask = hasWallpaperClosing,
+ )
+ )
}
}
+ /**
+ * Given this a closing task in a closing transition, a task is assumed to be closed by back
+ * navigation if:
+ * 1) Desktop mode is visible.
+ * 2) Task is in freeform.
+ * 3) Task is the latest task that the back gesture is triggered on.
+ * 4) It's not marked as a closing task as a result of closing it by the app header.
+ *
+ * This doesn't necessarily mean all the cases are because of back navigation but those cases
+ * will be rare. E.g. triggering back navigation on an app that pops up a close dialog, and
+ * closing it will minimize it here.
+ */
+ private fun getMinimizingTaskForClosingTransition(
+ taskInfo: ActivityManager.RunningTaskInfo
+ ): Int? {
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
+ val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId)
+ if (
+ visibleTaskCount > 0 &&
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
+ backAnimationController.latestTriggerBackTask == taskInfo.taskId &&
+ !desktopRepository.isClosingTask(taskInfo.taskId)
+ ) {
+ desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+ return taskInfo.taskId
+ }
+ return null
+ }
+
private fun removeWallpaperOnLastTaskClosingIfNeeded(
transition: IBinder,
info: TransitionInfo,
) {
+ // TODO: 380868195 - Smooth animation for wallpaper activity closing just by itself
for (change in info.changes) {
val taskInfo = change.taskInfo
if (taskInfo == null || taskInfo.taskId == -1) {
continue
}
+
val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
if (
- desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 1 &&
+ desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 0 &&
change.mode == TRANSIT_CLOSE &&
taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
desktopRepository.wallpaperActivityToken != null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index a17d55fdc2fe..4b59efbcecf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -30,8 +30,8 @@ import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
-import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
@@ -116,20 +116,18 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
DesktopModeStatus.canEnterDesktopMode(mContext)
- && mDesktopUserRepositories.isPresent()) {
+ && mDesktopUserRepositories.isPresent()) {
DesktopRepository repository =
mDesktopUserRepositories.get().getProfile(taskInfo.userId);
// TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
- || repository.isClosingTask(taskInfo.taskId)) {
+ || !repository.isMinimizedTask(taskInfo.taskId)) {
// A task that's vanishing should be removed:
- // - If it's closed by the X button which means it's marked as a closing task.
+ // - If it's not yet minimized. It can be minimized when a back navigation is
+ // triggered on a task and the task is closing. It will be marked as minimized in
+ // [DesktopTasksTransitionObserver] before it gets here.
repository.removeClosingTask(taskInfo.taskId);
repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
- } else {
- repository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */
- false);
- repository.minimizeTask(taskInfo.displayId, taskInfo.taskId);
}
}
mWindowDecorationViewModel.onTaskVanished(taskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 4c316de98744..f8e6285b0493 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -42,7 +42,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
-import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.IRemoteTransition;
@@ -444,10 +443,8 @@ public class KeyguardTransitionHandler
@Override
public void startKeyguardTransition(boolean keyguardShowing, boolean aodShowing) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- for (Display display : mDisplayController.getDisplays()) {
- wct.addKeyguardState(new KeyguardState.Builder(display.getDisplayId())
- .setKeyguardShowing(keyguardShowing).setAodShowing(aodShowing).build());
- }
+ wct.addKeyguardState(new KeyguardState.Builder().setKeyguardShowing(keyguardShowing)
+ .setAodShowing(aodShowing).build());
mMainExecutor.execute(() -> {
mTransitions.startTransition(keyguardShowing ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
wct, KeyguardTransitionHandler.this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 6e0e696e15fe..fc757ef6df96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -94,6 +94,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.SplitDragPolicy;
@@ -199,6 +200,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
private final Optional<DesktopTasksController> mDesktopTasksController;
private final MultiInstanceHelper mMultiInstanceHelpher;
+ private final SplitState mSplitState;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
@VisibleForTesting
@@ -228,6 +230,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
Optional<DesktopTasksController> desktopTasksController,
@Nullable StageCoordinator stageCoordinator,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
ShellExecutor mainExecutor,
Handler mainHandler) {
mShellCommandHandler = shellCommandHandler;
@@ -252,6 +255,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
mDesktopTasksController = desktopTasksController;
mStageCoordinator = stageCoordinator;
mMultiInstanceHelpher = multiInstanceHelper;
+ mSplitState = splitState;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
@@ -296,7 +300,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
- mWindowDecorViewModel);
+ mWindowDecorViewModel, mSplitState);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 07c157b4394c..b40996f52bd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -139,6 +139,7 @@ import com.android.wm.shell.common.split.OffscreenTouchZone;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
@@ -218,6 +219,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final Optional<RecentTasksController> mRecentTasks;
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
+ /** Singleton source of truth for the current state of split screen on this device. */
+ private final SplitState mSplitState;
private final Rect mTempRect1 = new Rect();
private final Rect mTempRect2 = new Rect();
@@ -344,7 +347,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor,
Handler mainHandler, Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -355,6 +358,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
+ mSplitState = splitState;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
@@ -412,7 +416,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
Handler mainHandler, Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -432,6 +436,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
+ mSplitState = splitState;
+
mDisplayController.addDisplayWindowListener(this);
transitions.addHandler(this);
mSplitUnsupportedToast = Toast.makeText(mContext,
@@ -1282,6 +1288,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(st -> {
+ mSplitLayout.updateStateWithCurrentPosition();
updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
// updateSurfaceBounds(), above, officially puts the two apps in their new
@@ -1437,6 +1444,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!isSplitActive() || mIsExiting) return;
onSplitScreenExit();
+ mSplitState.exit();
clearSplitPairedInRecents(exitReason);
mShouldUpdateRecents = false;
@@ -1632,6 +1640,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
}
deactivateSplit(wct, stageToTop);
+ mSplitState.exit();
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -1730,6 +1739,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
+ mSplitLayout.updateStateWithCurrentPosition();
mSplitLayout.update(null, true /* resetImePosition */);
if (enableFlexibleSplit()) {
runForActiveStages((stage) ->
@@ -1954,6 +1964,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ int currentSnapPosition = mSplitLayout.calculateCurrentSnapPosition();
+
if (Flags.enableFlexibleTwoAppSplit()) {
// Split screen can be laid out in such a way that some of the apps are offscreen.
// For the purposes of passing SplitBounds up to launcher (for use in thumbnails
@@ -1966,10 +1978,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
bottomRightBounds.top =
Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+
+ // TODO (b/349828130): Can change to getState() fully after brief soak time.
+ if (mSplitState.get() != currentSnapPosition) {
+ Log.wtf(TAG, "SplitState is " + mSplitState.get()
+ + ", expected " + currentSnapPosition);
+ currentSnapPosition = mSplitState.get();
+ }
}
SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
- leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
+ leftTopTaskId, rightBottomTaskId, currentSnapPosition);
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
@@ -2008,7 +2027,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mRootTaskInfo.configuration, this, mParentContainerCallbacks,
mDisplayController, mDisplayImeController, mTaskOrganizer,
- PARALLAX_ALIGN_CENTER /* parallaxType */, mMainHandler);
+ PARALLAX_ALIGN_CENTER /* parallaxType */, mSplitState, mMainHandler);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index 34681569a16c..c5e158c6b452 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -32,6 +32,7 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -60,6 +61,7 @@ public class TvSplitScreenController extends SplitScreenController {
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final LaunchAdjacentController mLaunchAdjacentController;
+ private final SplitState mSplitState;
private final Handler mMainHandler;
private final SystemWindows mSystemWindows;
@@ -80,6 +82,7 @@ public class TvSplitScreenController extends SplitScreenController {
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
@@ -87,8 +90,8 @@ public class TvSplitScreenController extends SplitScreenController {
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
- Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor,
- mainHandler);
+ Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, splitState,
+ mainExecutor, mainHandler);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -102,6 +105,7 @@ public class TvSplitScreenController extends SplitScreenController {
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
+ mSplitState = splitState;
mMainHandler = mainHandler;
mSystemWindows = systemWindows;
@@ -117,7 +121,7 @@ public class TvSplitScreenController extends SplitScreenController {
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool,
mIconProvider, mMainExecutor, mMainHandler,
- mRecentTasksOptional, mLaunchAdjacentController, mSystemWindows);
+ mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index 4451ee887363..ef1f88e635d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -28,6 +28,7 @@ import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.split.SplitScreenConstants;
@@ -53,10 +54,12 @@ public class TvStageCoordinator extends StageCoordinator
Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
+ SplitState splitState,
SystemWindows systemWindows) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty());
+ mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty(),
+ splitState);
mTvSplitMenuController = new TvSplitMenuController(context, this,
systemWindows, mainHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index a1e329af1543..1f03d7568130 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -37,6 +37,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.Cuj;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -44,6 +45,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
@@ -53,6 +55,9 @@ import java.util.function.Supplier;
* If the drag is repositioning, we update in the typical manner.
*/
public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.TransitionHandler {
+ // Timeout used for resize and drag CUJs, this is longer than the default timeout to avoid
+ // timing out in the middle of a resize or drag action.
+ private static final long LONG_CUJ_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10L);
private DesktopModeWindowDecoration mDesktopWindowDecoration;
private ShellTaskOrganizer mTaskOrganizer;
@@ -106,8 +111,8 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
mRepositionStartPoint.set(x, y);
if (isResizing()) {
// Capture CUJ for re-sizing window in DW mode.
- mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
- mDesktopWindowDecoration.mContext, mHandler, CUJ_DESKTOP_MODE_RESIZE_WINDOW);
+ mInteractionJankMonitor.begin(
+ createLongTimeoutJankConfigBuilder(CUJ_DESKTOP_MODE_RESIZE_WINDOW));
if (!mDesktopWindowDecoration.mHasGlobalFocus) {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true /* onTop */,
@@ -153,8 +158,8 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
}
} else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
// Begin window drag CUJ instrumentation only when drag position moves.
- mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
- mDesktopWindowDecoration.mContext, mHandler, CUJ_DESKTOP_MODE_DRAG_WINDOW);
+ mInteractionJankMonitor.begin(
+ createLongTimeoutJankConfigBuilder(CUJ_DESKTOP_MODE_DRAG_WINDOW));
final SurfaceControl.Transaction t = mTransactionSupplier.get();
DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y);
@@ -207,6 +212,14 @@ public class VeiledResizeTaskPositioner implements TaskPositioner, Transitions.T
}
}
+ private InteractionJankMonitor.Configuration.Builder createLongTimeoutJankConfigBuilder(
+ @Cuj.CujType int cujType) {
+ return InteractionJankMonitor.Configuration.Builder
+ .withSurface(cujType, mDesktopWindowDecoration.mContext,
+ mDesktopWindowDecoration.mTaskSurface, mHandler)
+ .setTimeout(LONG_CUJ_TIMEOUT_MS);
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index ddbc681f7cac..f40edaebec5e 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -266,5 +266,26 @@ test_module_config {
test_suites: ["device-tests"],
}
+test_module_config {
+ name: "WMShellFlickerTestsPip-nonMatchParent",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.*"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaExpandButtonTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaExpandButtonTest"],
+ test_suites: ["device-tests"],
+}
+
+test_module_config {
+ name: "WMShellFlickerTestsPip-BottomHalfExitPipToAppViaIntentTest",
+ base: "WMShellFlickerTestsPip",
+ include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaIntentTest"],
+ test_suites: ["device-tests"],
+}
+
// End breakdowns for WMShellFlickerTestsPip module
////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt
new file mode 100644
index 000000000000..c405b664e431
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppTransition.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.nonmatchparent
+
+import android.platform.test.annotations.Presubmit
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
+import org.junit.Test
+
+/**
+ * Base test class to verify PIP exit animation with an activity layout to the bottom half of
+ * the container.
+ */
+abstract class BottomHalfExitPipToAppTransition(flicker: LegacyFlickerTest) :
+ ExitPipToAppTransition(flicker) {
+
+ override val pipApp: PipAppHelper = BottomHalfPipAppHelper(instrumentation)
+
+ @Presubmit
+ @Test
+ override fun showBothAppLayersThenHidePip() {
+ // Disabled since the BottomHalfPipActivity just covers half of the simple activity.
+ }
+
+ @Presubmit
+ @Test
+ override fun showBothAppWindowsThenHidePip() {
+ // Disabled since the BottomHalfPipActivity just covers half of the simple activity.
+ }
+
+ @Presubmit
+ @Test
+ override fun pipAppCoversFullScreenAtEnd() {
+ // Disabled since the BottomHalfPipActivity just covers half of the simple activity.
+ }
+
+ /**
+ * Checks that the [testApp] and [pipApp] are always visible since the [pipApp] only covers
+ * half of screen.
+ */
+ @Presubmit
+ @Test
+ fun showBothAppLayersDuringPipTransition() {
+ flicker.assertLayers {
+ isVisible(testApp)
+ .isVisible(pipApp.or(ComponentNameMatcher.TRANSITION_SNAPSHOT))
+ }
+ }
+
+ /**
+ * Checks that the [testApp] and [pipApp] are always visible since the [pipApp] only covers
+ * half of screen.
+ */
+ @Presubmit
+ @Test
+ fun showBothAppWindowsDuringPipTransition() {
+ flicker.assertWm {
+ isAppWindowVisible(testApp)
+ .isAppWindowOnTop(pipApp)
+ .isAppWindowVisible(pipApp)
+ }
+ }
+
+ /**
+ * Verify that the [testApp] and [pipApp] covers the entire screen at the end of PIP exit
+ * animation since the [pipApp] will use a bottom half layout.
+ */
+ @Presubmit
+ @Test
+ fun testPlusPipAppCoversWindowFrameAtEnd() {
+ flicker.assertLayersEnd {
+ val pipRegion = visibleRegion(pipApp).region
+ visibleRegion(testApp).plus(pipRegion).coversExactly(displayBounds)
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt
new file mode 100644
index 000000000000..2a3dc07037df
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaExpandButtonTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.wm.shell.flicker.pip.nonmatchparent
+
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.window.flags.Flags
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test expanding a pip window back to bottom half layout via the expand button
+ *
+ * To run this test: `atest WMShellFlickerTestsPip:BottomHalfExitPipToAppViaExpandButtonTest`
+ *
+ * Actions:
+ * ```
+ * Launch an app in pip mode [bottomHalfPipApp],
+ * Launch another full screen mode [testApp]
+ * Expand [bottomHalfPipApp] app to bottom half layout by clicking on the pip window and
+ * then on the expand button
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2.
+@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
+@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class BottomHalfExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
+ BottomHalfExitPipToAppTransition(flicker)
+{
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.expandPipWindowToApp(wmHelper)
+ // Wait until the transition idle and test and pip app still shows.
+ wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp)
+ .withAppTransitionIdle().waitForAndVerify()
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
new file mode 100644
index 000000000000..8ed9cd23005b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.nonmatchparent
+
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.window.flags.Flags
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test expanding a pip window back to bottom half layout via an intent
+ *
+ * To run this test: `atest WMShellFlickerTestsPip:BottomHalfExitPipToAppViaIntentTest`
+ *
+ * Actions:
+ * ```
+ * Launch an app in pip mode [bottomHalfPipApp],
+ * Launch another full screen mode [testApp]
+ * Expand [bottomHalfPipApp] app to bottom half layout via an intent
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited from [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2.
+@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
+@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class BottomHalfExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) :
+ BottomHalfExitPipToAppTransition(flicker)
+{
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup {
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.exitPipToFullScreenViaIntent(wmHelper)
+ // Wait until the transition idle and test and pip app still shows.
+ wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp)
+ .withAppTransitionIdle().waitForAndVerify()
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
index cf69704a0470..fd3d3b5b6e2f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
@@ -56,6 +56,7 @@ public class DividerViewTest extends ShellTestCase {
private @Mock DisplayController mDisplayController;
private @Mock DisplayImeController mDisplayImeController;
private @Mock ShellTaskOrganizer mTaskOrganizer;
+ private @Mock SplitState mSplitState;
private @Mock Handler mHandler;
private SplitLayout mSplitLayout;
private DividerView mDividerView;
@@ -67,7 +68,7 @@ public class DividerViewTest extends ShellTestCase {
Configuration configuration = getConfiguration();
mSplitLayout = new SplitLayout("TestSplitLayout", mContext, configuration,
mSplitLayoutHandler, mCallbacks, mDisplayController, mDisplayImeController,
- mTaskOrganizer, SplitLayout.PARALLAX_NONE, mHandler);
+ mTaskOrganizer, SplitLayout.PARALLAX_NONE, mSplitState, mHandler);
SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager",
mContext,
configuration, mCallbacks);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index dc0f213338be..1904c43d78cf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -66,6 +66,7 @@ public class SplitLayoutTests extends ShellTestCase {
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock WindowContainerTransaction mWct;
+ @Mock SplitState mSplitState;
@Mock Handler mHandler;
@Captor ArgumentCaptor<Runnable> mRunnableCaptor;
private SplitLayout mSplitLayout;
@@ -83,6 +84,7 @@ public class SplitLayoutTests extends ShellTestCase {
mDisplayImeController,
mTaskOrganizer,
SplitLayout.PARALLAX_NONE,
+ mSplitState,
mHandler));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt
index 68d9bf9b926f..70341b6646c2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt
@@ -30,9 +30,7 @@ import org.mockito.Mockito.verify
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
-import org.mockito.kotlin.never
import org.mockito.kotlin.times
-import org.mockito.verification.VerificationMode
/**
* Tests for [LetterboxSurfaceBuilder].
@@ -140,7 +138,5 @@ class LetterboxSurfaceBuilderTest : ShellTestCase() {
val components = letterboxConfiguration.getBackgroundColorRgbArray()
verify(tx, expected.asMode()).setColor(anyOrNull(), eq(components))
}
-
- private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt
new file mode 100644
index 000000000000..29ea09aee61d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.verification.VerificationMode
+
+// Utility to make verification mode depending on a [Boolean].
+fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
index 07bfefe0b275..8825bb4956b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
@@ -43,10 +43,8 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.verification.VerificationMode
/**
* Tests for [LetterboxTransitionObserver].
@@ -292,14 +290,16 @@ class LetterboxTransitionObserverTest : ShellTestCase() {
expected: Boolean,
displayId: Int = DISPLAY_ID,
taskId: Int = TASK_ID,
- taskBounds: Rect = Rect()
+ taskBounds: Rect = Rect(),
+ activityBounds: Rect = Rect()
) = verify(
letterboxController,
expected.asMode()
).updateLetterboxSurfaceBounds(
eq(LetterboxKey(displayId, taskId)),
any<SurfaceControl.Transaction>(),
- eq(taskBounds)
+ eq(taskBounds),
+ eq(activityBounds)
)
fun createTopActivityChange(
@@ -334,7 +334,5 @@ class LetterboxTransitionObserverTest : ShellTestCase() {
this.displayId = displayId
}, changeMode = TRANSIT_CLOSE)
}
-
- private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 2d5544527436..267bbb6c51e8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -152,7 +152,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS)
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
fun startRemoveTransition_callsFreeformTaskTransitionHandler() {
val wct = WindowContainerTransaction()
whenever(freeformTaskTransitionHandler.startRemoveTransition(wct))
@@ -164,7 +166,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
fun startRemoveTransition_startsCloseTransition() {
val wct = WindowContainerTransaction()
whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler))
@@ -203,7 +207,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() {
val wct = WindowContainerTransaction()
val transition = mock<IBinder>()
@@ -231,7 +237,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX)
fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() {
val wct = WindowContainerTransaction()
val transition = mock<IBinder>()
@@ -272,7 +280,8 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
@Test
@DisableFlags(
Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
- Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun startLaunchTransition_immersiveAndAppLaunchFlagsDisabled_doesNotUseMixedHandler() {
val wct = WindowContainerTransaction()
val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -308,7 +317,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun startLaunchTransition_desktopAppLaunchEnabled_usesMixedHandler() {
val wct = WindowContainerTransaction()
val task = createTask(WINDOWING_MODE_FREEFORM)
@@ -407,7 +418,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun startAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -437,7 +450,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun startAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -469,7 +484,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun startAnimation_pendingTransition_noLaunchChange_returnsFalse() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -566,7 +583,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
@@ -598,7 +617,9 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS,
+ Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX)
fun addPendingAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
val wct = WindowContainerTransaction()
val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 238483da537c..b31a3f5fa642 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -39,6 +39,7 @@ import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -67,9 +68,7 @@ class DesktopTasksTransitionObserverTest {
@JvmField
@Rule
val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeStatus::class.java)
- .build()!!
+ ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!
private val testExecutor = mock<ShellExecutor>()
private val mockShellInit = mock<ShellInit>()
@@ -79,6 +78,7 @@ class DesktopTasksTransitionObserverTest {
private val userRepositories = mock<DesktopUserRepositories>()
private val taskRepository = mock<DesktopRepository>()
private val mixedHandler = mock<DesktopMixedTransitionHandler>()
+ private val backAnimationController = mock<BackAnimationController>()
private lateinit var transitionObserver: DesktopTasksTransitionObserver
private lateinit var shellInit: ShellInit
@@ -93,7 +93,13 @@ class DesktopTasksTransitionObserverTest {
transitionObserver =
DesktopTasksTransitionObserver(
- context, userRepositories, transitions, shellTaskOrganizer, mixedHandler, shellInit
+ context,
+ userRepositories,
+ transitions,
+ shellTaskOrganizer,
+ mixedHandler,
+ backAnimationController,
+ shellInit
)
}
@@ -105,8 +111,7 @@ class DesktopTasksTransitionObserverTest {
transitionObserver.onTransitionReady(
transition = mock(),
- info =
- createBackNavigationTransition(task),
+ info = createBackNavigationTransition(task),
startTransaction = mock(),
finishTransaction = mock(),
)
@@ -117,14 +122,59 @@ class DesktopTasksTransitionObserverTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun backNavigation_withCloseTransitionNotLastTask_taskMinimized() {
+ val task = createTaskInfo(1)
+ val transition = mock<IBinder>()
+ whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(2)
+ whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
+ whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = transition,
+ info = createBackNavigationTransition(task, TRANSIT_CLOSE),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+ val pendingTransition =
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition, task.taskId, isLastTask = false)
+ verify(mixedHandler).addPendingMixedTransition(pendingTransition)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun backNavigation_withCloseTransitionLastTask_taskMinimized() {
+ val task = createTaskInfo(1)
+ val transition = mock<IBinder>()
+ whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+ whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
+ whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = transition,
+ info = createBackNavigationTransition(task, TRANSIT_CLOSE, true),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+ val pendingTransition =
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition, task.taskId, isLastTask = true)
+ verify(mixedHandler).addPendingMixedTransition(pendingTransition)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun backNavigation_nullTaskInfo_taskNotMinimized() {
val task = createTaskInfo(1)
whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
transitionObserver.onTransitionReady(
transition = mock(),
- info =
- createBackNavigationTransition(null),
+ info = createBackNavigationTransition(null),
startTransaction = mock(),
finishTransaction = mock(),
)
@@ -173,7 +223,7 @@ class DesktopTasksTransitionObserverTest {
val mockTransition = Mockito.mock(IBinder::class.java)
val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
val wallpaperToken = MockToken().token()
- whenever(taskRepository.getVisibleTaskCount(task.displayId)).thenReturn(1)
+ whenever(taskRepository.getVisibleTaskCount(task.displayId)).thenReturn(0)
whenever(taskRepository.wallpaperActivityToken).thenReturn(wallpaperToken)
transitionObserver.onTransitionReady(
@@ -190,17 +240,27 @@ class DesktopTasksTransitionObserverTest {
}
private fun createBackNavigationTransition(
- task: RunningTaskInfo?
+ task: RunningTaskInfo?,
+ type: Int = TRANSIT_TO_BACK,
+ withWallpaper: Boolean = false,
): TransitionInfo {
- return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+ return TransitionInfo(type, 0 /* flags */).apply {
addChange(
Change(mock(), mock()).apply {
- mode = TRANSIT_TO_BACK
+ mode = type
parent = null
taskInfo = task
flags = flags
- }
- )
+ })
+ if (withWallpaper) {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_CLOSE
+ parent = null
+ taskInfo = createWallpaperTaskInfo()
+ flags = flags
+ })
+ }
}
}
@@ -215,14 +275,11 @@ class DesktopTasksTransitionObserverTest {
parent = null
taskInfo = task
flags = flags
- }
- )
+ })
}
}
- private fun createCloseTransition(
- task: RunningTaskInfo?
- ): TransitionInfo {
+ private fun createCloseTransition(task: RunningTaskInfo?): TransitionInfo {
return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply {
addChange(
Change(mock(), mock()).apply {
@@ -230,8 +287,7 @@ class DesktopTasksTransitionObserverTest {
parent = null
taskInfo = task
flags = flags
- }
- )
+ })
}
}
@@ -243,8 +299,7 @@ class DesktopTasksTransitionObserverTest {
if (handlerClass == null) {
Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isNull())
} else {
- Mockito.verify(transitions)
- .startTransition(eq(type), arg.capture(), isA(handlerClass))
+ Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
}
return arg.value
}
@@ -268,8 +323,15 @@ class DesktopTasksTransitionObserverTest {
displayId = DEFAULT_DISPLAY
configuration.windowConfiguration.windowingMode = windowingMode
token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
- baseIntent = Intent().apply {
- component = ComponentName("package", "component.name")
- }
+ baseIntent = Intent().apply { component = ComponentName("package", "component.name") }
+ }
+
+ private fun createWallpaperTaskInfo() =
+ RunningTaskInfo().apply {
+ token = mock<WindowContainerToken>()
+ baseIntent =
+ Intent().apply {
+ component = DesktopWallpaperActivity.wallpaperActivityComponent
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 794ba48e2581..b8629b2f00d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -47,8 +47,8 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
-import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -212,10 +212,11 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
@DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
- public void onTaskVanished_nonClosingTask_noTransitionObservers_isMinimized() {
+ public void onTaskVanished_minimizedTask_noTransitionObservers_isNotRemoved() {
ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
task.isVisible = true;
+ when(mDesktopRepository.isMinimizedTask(task.taskId)).thenReturn(true);
mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -223,7 +224,8 @@ public final class FreeformTaskListenerTests extends ShellTestCase {
task.displayId = INVALID_DISPLAY;
mFreeformTaskListener.onTaskVanished(task);
- verify(mDesktopUserRepositories.getCurrent()).minimizeTask(task.displayId, task.taskId);
+ verify(mDesktopUserRepositories.getCurrent(), never()).removeFreeformTask(task.displayId,
+ task.taskId);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 72a7a3f5ec99..7726c97b0ce3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -71,6 +71,7 @@ import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
@@ -119,6 +120,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock WindowDecorViewModel mWindowDecorViewModel;
@Mock DesktopTasksController mDesktopTasksController;
@Mock MultiInstanceHelper mMultiInstanceHelper;
+ @Mock SplitState mSplitState;
@Captor ArgumentCaptor<Intent> mIntentCaptor;
private ShellController mShellController;
@@ -136,7 +138,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
- mStageCoordinator, mMultiInstanceHelper, mMainExecutor, mMainHandler));
+ mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index d13a8888edc7..1a2d60ddad3e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -35,6 +35,7 @@ import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.transition.Transitions;
@@ -80,11 +81,11 @@ public class SplitTestUtils {
ShellExecutor mainExecutor, Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
- launchAdjacentController, windowDecorViewModel);
+ launchAdjacentController, windowDecorViewModel, splitState);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
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 e32cf3899a03..de77837cb0e5 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
@@ -78,6 +78,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.TestRemoteTransition;
@@ -108,6 +109,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private Transitions mTransitions;
@Mock private IconProvider mIconProvider;
@Mock private WindowDecorViewModel mWindowDecorViewModel;
+ @Mock private SplitState mSplitState;
@Mock private ShellExecutor mMainExecutor;
@Mock private Handler mMainHandler;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@@ -144,7 +146,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
- mLaunchAdjacentController, Optional.empty());
+ mLaunchAdjacentController, Optional.empty(), mSplitState);
mStageCoordinator.setMixedHandler(mMixedHandler);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 1e739cd446ae..7afcce1243e3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -71,6 +71,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.sysui.ShellController;
@@ -116,6 +117,8 @@ public class StageCoordinatorTests extends ShellTestCase {
private LaunchAdjacentController mLaunchAdjacentController;
@Mock
private DefaultMixedHandler mDefaultMixedHandler;
+ @Mock
+ private SplitState mSplitState;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -139,7 +142,7 @@ public class StageCoordinatorTests extends ShellTestCase {
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
- Optional.empty()));
+ Optional.empty(), mSplitState));
mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index cf3f74085d66..8cd08d3aad6c 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -642,6 +642,14 @@ package android.location.provider {
method public void onFlushComplete();
}
+ @FlaggedApi("android.location.flags.population_density_provider") public abstract class PopulationDensityProviderBase {
+ ctor public PopulationDensityProviderBase(@NonNull android.content.Context, @NonNull String);
+ method @Nullable public final android.os.IBinder getBinder();
+ method public abstract void onGetCoarsenedS2Cell(double, double, @NonNull android.os.OutcomeReceiver<long[],java.lang.Throwable>);
+ method public abstract void onGetDefaultCoarseningLevel(@NonNull android.os.OutcomeReceiver<java.lang.Integer,java.lang.Throwable>);
+ field public static final String ACTION_POPULATION_DENSITY_PROVIDER = "com.android.location.service.PopulationDensityProvider";
+ }
+
public final class ProviderRequest implements android.os.Parcelable {
method public int describeContents();
method @IntRange(from=0) public long getIntervalMillis();
diff --git a/location/java/android/location/provider/IPopulationDensityProvider.aidl b/location/java/android/location/provider/IPopulationDensityProvider.aidl
new file mode 100644
index 000000000000..9b5cb5ae8c7a
--- /dev/null
+++ b/location/java/android/location/provider/IPopulationDensityProvider.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.os.Bundle;
+
+import android.location.Location;
+import android.location.provider.IS2CellIdsCallback;
+import android.location.provider.IS2LevelCallback;
+
+/**
+ * Binder interface for services that implement a population density provider. Do not implement this
+ * directly, extend {@link PopulationDensityProviderBase} instead.
+ * @hide
+ */
+oneway interface IPopulationDensityProvider {
+ /**
+ * Gets the default S2 level to be used to coarsen any location, in case a more precise answer
+ * from the method below can't be obtained.
+ */
+ void getDefaultCoarseningLevel(in IS2LevelCallback callback);
+
+ /**
+ * Returns a list of IDs of the S2 cells to be used to coarsen a location. The answer should
+ * contain at least one S2 cell, which should contain the requested location. Its level
+ * represents the population density. Optionally, additional nearby cells can be also returned,
+ * to assist in coarsening nearby locations.
+ */
+ void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees, in IS2CellIdsCallback
+ callback);
+}
diff --git a/location/java/android/location/provider/IS2CellIdsCallback.aidl b/location/java/android/location/provider/IS2CellIdsCallback.aidl
new file mode 100644
index 000000000000..f583045ebb26
--- /dev/null
+++ b/location/java/android/location/provider/IS2CellIdsCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.Location;
+
+/**
+ * Binder interface for S2 cell IDs callbacks.
+ * @hide
+ */
+oneway interface IS2CellIdsCallback {
+
+ /**
+ * Called with the resulting list of S2 cell IDs. The first cell is expected to contain
+ * the requested latitude/longitude. Its level represent the population density. Optionally,
+ * the list can also contain additional nearby cells.
+ */
+ void onResult(in long[] s2CellIds);
+
+ /** Called if any error occurs while processing the query. */
+ void onError();
+}
diff --git a/location/java/android/location/provider/IS2LevelCallback.aidl b/location/java/android/location/provider/IS2LevelCallback.aidl
new file mode 100644
index 000000000000..49f96ef7e3e2
--- /dev/null
+++ b/location/java/android/location/provider/IS2LevelCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.Location;
+
+/**
+ * Binder interface for S2 level callback.
+ * @hide
+ */
+oneway interface IS2LevelCallback {
+ /**
+ * Called with the resulting default S2 level for coarsening a location, in case a better
+ * answer cannot be obtained for a latitude/longitude.
+ */
+ void onResult(int s2Level);
+
+ /** Called if any error occurs while processing the query. */
+ void onError();
+}
diff --git a/location/java/android/location/provider/PopulationDensityProviderBase.java b/location/java/android/location/provider/PopulationDensityProviderBase.java
new file mode 100644
index 000000000000..3907516f6aaa
--- /dev/null
+++ b/location/java/android/location/provider/PopulationDensityProviderBase.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A provider for population density.
+ * The population density is defined as the S2 level at which the S2 cell around the latitude /
+ * longitude contains at least a thousand people.
+ * It exposes two methods: one about providing population density around a latitude / longitude,
+ * and one about providing a "default" population density to fall back to in case the first API
+ * can't be used or returns an error.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_POPULATION_DENSITY_PROVIDER)
+public abstract class PopulationDensityProviderBase {
+
+ final String mTag;
+ final @Nullable String mAttributionTag;
+ final IBinder mBinder;
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the
+ * PopulationDensity provider.
+ */
+ @SuppressLint("ActionValue")
+ public static final String ACTION_POPULATION_DENSITY_PROVIDER =
+ "com.android.location.service.PopulationDensityProvider";
+
+ public PopulationDensityProviderBase(@NonNull Context context, @NonNull String tag) {
+ mTag = tag;
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new Service();
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the
+ * {@link android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ public final @Nullable IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Called upon receiving a new request for the default coarsening level.
+ * The callback {@link OutcomeReceiver#onResult} should be called with the result; or, in case
+ * an error occurs, {@link OutcomeReceiver#onError} should be called.
+ * The callback is single-use, calling more than any one of these two methods throws an
+ * AssertionException.
+ *
+ * @param callback A single-use callback that either returns the coarsening level, or an error.
+ */
+ public abstract void onGetDefaultCoarseningLevel(@NonNull OutcomeReceiver<Integer, Throwable>
+ callback);
+
+ /**
+ * Called upon receiving a new request for population density at a specific latitude/longitude,
+ * expressed in degrees.
+ * The answer is at least one S2CellId corresponding to the coarsening level at the specified
+ * location. This must be the first element of the result array. Optionally, additional nearby
+ * S2CellIds can be returned. One use for the optional nearby cells is when the client has a
+ * local cache that needs to be filled with the local area around a certain latitude/longitude.
+ * The callback {@link OutcomeReceiver#onResult} should be called with the result; or, in case
+ * an error occurs, {@link OutcomeReceiver#onError} should be called.
+ * The callback is single-use, calling more than any one of these two methods throws an
+ * AssertionException.
+ *
+ * @param callback A single-use callback that either returns S2CellIds, or an error.
+ */
+ public abstract void onGetCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
+ @NonNull OutcomeReceiver<long[], Throwable> callback);
+
+ private final class Service extends IPopulationDensityProvider.Stub {
+ @Override
+ public void getDefaultCoarseningLevel(@NonNull IS2LevelCallback callback) {
+ try {
+ onGetDefaultCoarseningLevel(new SingleUseS2LevelCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+
+ @Override
+ public void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
+ @NonNull IS2CellIdsCallback callback) {
+ try {
+ onGetCoarsenedS2Cell(latitudeDegrees, longitudeDegrees,
+ new SingleUseS2CellIdsCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+ }
+
+ private static class SingleUseS2LevelCallback implements OutcomeReceiver<Integer, Throwable> {
+
+ private final AtomicReference<IS2LevelCallback> mCallback;
+
+ SingleUseS2LevelCallback(IS2LevelCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onResult(Integer level) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResult(level);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private static class SingleUseS2CellIdsCallback implements OutcomeReceiver<long[], Throwable> {
+
+ private final AtomicReference<IS2CellIdsCallback> mCallback;
+
+ SingleUseS2CellIdsCallback(IS2CellIdsCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onResult(long[] s2CellIds) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResult(s2CellIds);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index d433ec876af9..dd5067a3ee67 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -24,6 +24,7 @@ import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_S
import static com.android.media.flags.Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -616,6 +617,7 @@ public final class MediaRoute2Info implements Parcelable {
private final String mProviderId;
private final boolean mIsVisibilityRestricted;
private final Set<String> mAllowedPackages;
+ private final Set<String> mRequiredPermissions;
@SuitabilityStatus private final int mSuitabilityStatus;
MediaRoute2Info(@NonNull Builder builder) {
@@ -640,6 +642,7 @@ public final class MediaRoute2Info implements Parcelable {
mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
mAllowedPackages = builder.mAllowedPackages;
mSuitabilityStatus = builder.mSuitabilityStatus;
+ mRequiredPermissions = Set.copyOf(builder.mRequiredPermissions);
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -664,6 +667,7 @@ public final class MediaRoute2Info implements Parcelable {
mProviderId = in.readString();
mIsVisibilityRestricted = in.readBoolean();
mAllowedPackages = Set.of(in.createString8Array());
+ mRequiredPermissions = Set.of(in.createString8Array());
mSuitabilityStatus = in.readInt();
}
@@ -907,6 +911,15 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * @return the set of permissions which must be held to see this route
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
+ public Set<String> getRequiredPermissions() {
+ return mRequiredPermissions;
+ }
+
+ /**
* Returns whether this route's type can only be published by the system route provider.
*
* @see #isSystemRoute()
@@ -977,6 +990,7 @@ public final class MediaRoute2Info implements Parcelable {
pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
+ pw.println(indent + "mRequiredPermissions=" + mRequiredPermissions);
}
private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -1013,6 +1027,7 @@ public final class MediaRoute2Info implements Parcelable {
&& Objects.equals(mProviderId, other.mProviderId)
&& (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
&& Objects.equals(mAllowedPackages, other.mAllowedPackages)
+ && Objects.equals(mRequiredPermissions, other.mRequiredPermissions)
&& mSuitabilityStatus == other.mSuitabilityStatus;
}
@@ -1039,6 +1054,7 @@ public final class MediaRoute2Info implements Parcelable {
mProviderId,
mIsVisibilityRestricted,
mAllowedPackages,
+ mRequiredPermissions,
mSuitabilityStatus);
}
@@ -1079,6 +1095,8 @@ public final class MediaRoute2Info implements Parcelable {
.append(mIsVisibilityRestricted)
.append(", allowedPackages=")
.append(String.join(",", mAllowedPackages))
+ .append(", mRequiredPermissions=")
+ .append(String.join(",", mRequiredPermissions))
.append(", suitabilityStatus=")
.append(mSuitabilityStatus)
.append(" }")
@@ -1112,6 +1130,7 @@ public final class MediaRoute2Info implements Parcelable {
dest.writeString(mProviderId);
dest.writeBoolean(mIsVisibilityRestricted);
dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
+ dest.writeString8Array(mRequiredPermissions.toArray(new String[0]));
dest.writeInt(mSuitabilityStatus);
}
@@ -1260,6 +1279,7 @@ public final class MediaRoute2Info implements Parcelable {
private String mProviderId;
private boolean mIsVisibilityRestricted;
private Set<String> mAllowedPackages;
+ private Set<String> mRequiredPermissions;
@SuitabilityStatus private int mSuitabilityStatus;
/**
@@ -1285,6 +1305,7 @@ public final class MediaRoute2Info implements Parcelable {
mDeduplicationIds = Set.of();
mAllowedPackages = Set.of();
mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+ mRequiredPermissions = Set.of();
}
/**
@@ -1334,6 +1355,7 @@ public final class MediaRoute2Info implements Parcelable {
mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
mAllowedPackages = routeInfo.mAllowedPackages;
mSuitabilityStatus = routeInfo.mSuitabilityStatus;
+ mRequiredPermissions = routeInfo.mRequiredPermissions;
}
/**
@@ -1565,6 +1587,7 @@ public final class MediaRoute2Info implements Parcelable {
public Builder setVisibilityPublic() {
mIsVisibilityRestricted = false;
mAllowedPackages = Set.of();
+ mRequiredPermissions = Set.of();
return this;
}
@@ -1589,6 +1612,19 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Limits the visibility of this route to holders of a set of permissions.
+ *
+ * @param requiredPermissions the list of all permissions which must be held in order to
+ * see this route.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
+ public Builder setRequiredPermissions(@NonNull Set<String> requiredPermissions) {
+ mRequiredPermissions = Set.copyOf(requiredPermissions);
+ return this;
+ }
+
+ /**
* Sets route suitability status.
*
* <p>The default value is {@link
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index a14f1fd15f27..547099f044b2 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -19,6 +19,7 @@ package android.media;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,7 @@ import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.media.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -81,6 +83,22 @@ public abstract class MediaRoute2ProviderService extends Service {
public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
/**
+ * {@link Intent} action that indicates that the declaring service supports routing of the
+ * system media.
+ *
+ * <p>Providers must include this action if they intend to publish routes that support the
+ * system media, as described by {@link MediaRoute2Info#getSupportedRoutingTypes()}.
+ *
+ * @see #onCreateSystemRoutingSession
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE_SYSTEM_MEDIA =
+ "android.media.MediaRoute2ProviderService.SYSTEM_MEDIA";
+
+ /**
* A category indicating that the associated provider is only intended for use within the app
* that hosts the provider.
*
@@ -138,12 +156,26 @@ public abstract class MediaRoute2ProviderService extends Service {
public static final int REASON_INVALID_COMMAND = 4;
/**
+ * The request has failed because the requested operation is not implemented by the provider.
+ *
+ * @see #notifyRequestFailed
* @hide
*/
- @IntDef(prefix = "REASON_", value = {
- REASON_UNKNOWN_ERROR, REASON_REJECTED, REASON_NETWORK_ERROR, REASON_ROUTE_NOT_AVAILABLE,
- REASON_INVALID_COMMAND
- })
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public static final int REASON_UNIMPLEMENTED = 5;
+
+ /** @hide */
+ @IntDef(
+ prefix = "REASON_",
+ value = {
+ REASON_UNKNOWN_ERROR,
+ REASON_REJECTED,
+ REASON_NETWORK_ERROR,
+ REASON_ROUTE_NOT_AVAILABLE,
+ REASON_INVALID_COMMAND,
+ REASON_UNIMPLEMENTED
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
@@ -282,6 +314,32 @@ public abstract class MediaRoute2ProviderService extends Service {
}
/**
+ * Notifies the system of the successful creation of a system media routing session.
+ *
+ * <p>This method can only be called as the result of a prior call to {@link
+ * #onCreateSystemRoutingSession}.
+ *
+ * @param requestId the ID of the {@link #onCreateSystemRoutingSession} request which this call
+ * is in response to.
+ * @param sessionInfo a {@link RoutingSessionInfo} that describes the newly created routing
+ * session.
+ * @param formats the {@link MediaStreamsFormats} that describes the format for the {@link
+ * MediaStreams} to return.
+ * @return a {@link MediaStreams} instance that holds the media streams to route as part of the
+ * newly created routing session.
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ @NonNull
+ public final MediaStreams notifySystemMediaSessionCreated(
+ long requestId,
+ @NonNull RoutingSessionInfo sessionInfo,
+ @NonNull MediaStreamsFormats formats) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Notifies the existing session is updated. For example, when
* {@link RoutingSessionInfo#getSelectedRoutes() selected routes} are changed.
*/
@@ -399,6 +457,43 @@ public abstract class MediaRoute2ProviderService extends Service {
@NonNull String routeId, @Nullable Bundle sessionHints);
/**
+ * Called when the service receives a request to create a system routing session.
+ *
+ * <p>This method will only be called for routes that support routing of the system media, as
+ * described by {@link MediaRoute2Info#getSupportedRoutingTypes()}.
+ *
+ * <p>Implementors of this method must call {@link #notifySystemMediaSessionCreated} with the
+ * given {@code requestId} to indicate a successful session creation. If the session creation
+ * fails (for example, if the connection to the receiver device fails), the implementor must
+ * call {@link #notifyRequestFailed}, passing the {@code requestId}.
+ *
+ * <p>Unlike {@link #onCreateSession}, system sessions route the system media (for example,
+ * audio and/or video) which is to be retrieved by calling {@link
+ * #notifySystemMediaSessionCreated}.
+ *
+ * <p>Changes to the session can be notified by calling {@link #notifySessionUpdated}.
+ *
+ * @param requestId the ID of this request
+ * @param packageName the package name of the application whose media to route.
+ * @param routeId the ID of the route initially being {@link
+ * RoutingSessionInfo#getSelectedRoutes() selected}.
+ * @param sessionHints an optional bundle of arguments sent by {@link MediaRouter2}, or null if
+ * none.
+ * @see RoutingSessionInfo.Builder
+ * @see #notifySystemMediaSessionCreated
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public void onCreateSystemRoutingSession(
+ long requestId,
+ @NonNull String packageName,
+ @NonNull String routeId,
+ @Nullable Bundle sessionHints) {
+ mHandler.post(() -> notifyRequestFailed(requestId, REASON_UNIMPLEMENTED));
+ }
+
+ /**
* Called when the session should be released. A client of the session or system can request
* a session to be released.
* <p>
@@ -735,4 +830,100 @@ public abstract class MediaRoute2ProviderService extends Service {
MediaRoute2ProviderService.this, requestId, sessionId));
}
}
+
+ /**
+ * Holds the streams to be routed as part of a system media routing session.
+ *
+ * <p>The encoded data format matches the {@link MediaStreamsFormats} passed to {@link
+ * #notifySystemMediaSessionCreated}.
+ *
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public static final class MediaStreams {
+
+ private final AudioRecord mAudioRecord;
+
+ // TODO: b/380431086: Add the video equivalent.
+
+ private MediaStreams(AudioRecord mAudioRecord) {
+ this.mAudioRecord = mAudioRecord;
+ }
+
+ /**
+ * Returns the {@link AudioRecord} from which to read the audio data to route, or null if
+ * the routing session doesn't include audio.
+ */
+ @Nullable
+ public AudioRecord getAudioRecord() {
+ return mAudioRecord;
+ }
+ }
+
+ /**
+ * Holds the formats to encode media data to be read from {@link MediaStreams}.
+ *
+ * @see MediaStreams
+ * @see #notifySystemMediaSessionCreated
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public static final class MediaStreamsFormats {
+
+ private final AudioFormat mAudioFormat;
+
+ // TODO: b/380431086: Add the video equivalent.
+
+ private MediaStreamsFormats(Builder builder) {
+ this.mAudioFormat = builder.mAudioFormat;
+ }
+
+ /**
+ * Returns the audio format to use for creating the {@link MediaStreams#getAudioRecord} to
+ * return from {@link #notifySystemMediaSessionCreated}.
+ *
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public AudioFormat getAudioFormat() {
+ return mAudioFormat;
+ }
+
+ /**
+ * Builder for {@link MediaStreamsFormats}
+ *
+ * @hide
+ */
+ // TODO: b/362507305 - Unhide once the implementation and CTS are in place.
+ @FlaggedApi(Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2)
+ public static final class Builder {
+ private AudioFormat mAudioFormat;
+
+ /**
+ * Sets the audio format to use for creating the {@link MediaStreams#getAudioRecord} to
+ * return from {@link #notifySystemMediaSessionCreated}.
+ *
+ * @param audioFormat the audio format
+ * @return this builder
+ */
+ @NonNull
+ public Builder setAudioFormat(@NonNull AudioFormat audioFormat) {
+ this.mAudioFormat = Objects.requireNonNull(audioFormat);
+ return this;
+ }
+
+ /**
+ * Builds the {@link MediaStreamsFormats} instance.
+ *
+ * @return the built {@link MediaStreamsFormats} instance
+ */
+ @NonNull
+ public MediaStreamsFormats build() {
+ return new MediaStreamsFormats(this);
+ }
+ }
+ }
}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 65e83b9bf204..8fe543656ed9 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -328,7 +328,7 @@ public final class SoundTriggerDetector {
mRecognitionCallback,
new RecognitionConfig.Builder()
.setCaptureRequested(captureTriggerAudio)
- .setAllowMultipleTriggers(allowMultipleTriggers)
+ .setMultipleTriggersAllowed(allowMultipleTriggers)
.setAudioCapabilities(audioCapabilities)
.build(),
runInBatterySaver);
diff --git a/native/android/OWNERS b/native/android/OWNERS
index f0db2ea236ea..1fde7d268517 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -2,7 +2,7 @@ jreck@google.com #{LAST_RESORT_SUGGESTION}
# General NDK API reviewers
per-file libandroid.map.txt = danalbert@google.com, etalvala@google.com, michaelwr@google.com
-per-file libandroid.map.txt = jreck@google.com, zyy@google.com
+per-file libandroid.map.txt = jreck@google.com, zyy@google.com, mattbuckley@google.com
# Networking
per-file libandroid_net.map.txt, net.c = set noparent
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 7f555a868615..e8644ee1a73c 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -301,7 +301,6 @@ LIBANDROID {
ASurfaceTransaction_setEnableBackPressure; # introduced=31
ASurfaceTransaction_setFrameRate; # introduced=30
ASurfaceTransaction_setFrameRateWithChangeStrategy; # introduced=31
- ASurfaceTransaction_setFrameRateParams; # introduced=36
ASurfaceTransaction_clearFrameRate; # introduced=34
ASurfaceTransaction_setFrameTimeline; # introduced=Tiramisu
ASurfaceTransaction_setGeometry; # introduced=29
@@ -376,6 +375,7 @@ LIBANDROID {
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
APerformanceHint_borrowSessionFromJava; # introduced=36
+ APerformanceHint_setNativeSurfaces; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -388,6 +388,8 @@ LIBANDROID {
ASessionCreationConfig_setTargetWorkDurationNanos; # introduced=36
ASessionCreationConfig_setPreferPowerEfficiency; # introduced=36
ASessionCreationConfig_setGraphicsPipeline; # introduced=36
+ ASessionCreationConfig_setNativeSurfaces; # introduced=36
+ ASessionCreationConfig_setUseAutoTiming; # introduced=36
local:
*;
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 883e139cca0a..608c01caee96 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -29,13 +29,19 @@
#include <aidl/android/os/SessionCreationConfig.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
+#include <android/native_window.h>
#include <android/performance_hint.h>
+#include <android/surface_control.h>
#include <android/trace.h>
#include <android_os.h>
#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
#include <inttypes.h>
#include <jni_wrappers.h>
#include <performance_hint_private.h>
@@ -66,7 +72,12 @@ struct APerformanceHintSession;
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
-struct ASessionCreationConfig : public SessionCreationConfig {};
+struct ASessionCreationConfig : public SessionCreationConfig {
+ std::vector<wp<IBinder>> layers{};
+ bool hasMode(hal::SessionMode&& mode) {
+ return std::find(modesToEnable.begin(), modesToEnable.end(), mode) != modesToEnable.end();
+ }
+};
bool kForceGraphicsPipeline = false;
@@ -158,6 +169,11 @@ public:
FMQWrapper& getFMQWrapper();
bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
void initJava(JNIEnv* _Nonnull env);
+ ndk::ScopedAIBinder_Weak x;
+ template <class T>
+ static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls, int numSurfaceControls,
+ std::vector<T>& out);
private:
// Necessary to create an empty binder object
@@ -203,6 +219,8 @@ public:
int setPreferPowerEfficiency(bool enabled);
int reportActualWorkDuration(AWorkDuration* workDuration);
bool isJava();
+ status_t setNativeSurfaces(ANativeWindow** windows, int numWindows, ASurfaceControl** controls,
+ int numSurfaceControls);
private:
friend struct APerformanceHintManager;
@@ -231,7 +249,7 @@ private:
static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
std::vector<int32_t> mLastThreadIDs GUARDED_BY(sHintMutex);
- std::optional<hal::SessionConfig> mSessionConfig GUARDED_BY(sHintMutex);
+ std::optional<hal::SessionConfig> mSessionConfig;
// Tracing helpers
void traceThreads(const std::vector<int32_t>& tids) REQUIRES(sHintMutex);
void tracePowerEfficient(bool powerEfficient);
@@ -329,14 +347,12 @@ APerformanceHintSession* APerformanceHintManager::createSession(
ndk::ScopedAStatus ret;
hal::SessionConfig sessionConfig{.id = -1};
- SessionCreationConfig creationConfig{
+ ASessionCreationConfig creationConfig{{
.tids = std::vector<int32_t>(threadIds, threadIds + size),
.targetWorkDurationNanos = initialTargetWorkDurationNanos,
- };
+ }};
- return APerformanceHintManager::createSessionUsingConfig(static_cast<ASessionCreationConfig*>(
- &creationConfig),
- tag, isJava);
+ return APerformanceHintManager::createSessionUsingConfig(&creationConfig, tag, isJava);
}
APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
@@ -345,11 +361,29 @@ APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
hal::SessionConfig sessionConfig{.id = -1};
ndk::ScopedAStatus ret;
+ // Hold the tokens weakly until we actually need them,
+ // then promote them, then drop all strong refs after
+ if (!sessionCreationConfig->layers.empty()) {
+ for (auto&& layerIter = sessionCreationConfig->layers.begin();
+ layerIter != sessionCreationConfig->layers.end();) {
+ sp<IBinder> promoted = layerIter->promote();
+ if (promoted == nullptr) {
+ layerIter = sessionCreationConfig->layers.erase(layerIter);
+ } else {
+ sessionCreationConfig->layerTokens.push_back(
+ ndk::SpAIBinder(AIBinder_fromPlatformBinder(promoted.get())));
+ ++layerIter;
+ }
+ }
+ }
+
ret = mHintManager->createHintSessionWithConfig(mToken, tag,
*static_cast<SessionCreationConfig*>(
sessionCreationConfig),
&sessionConfig, &session);
+ sessionCreationConfig->layerTokens.clear();
+
if (!ret.isOk() || !session) {
ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage());
return nullptr;
@@ -679,6 +713,57 @@ int APerformanceHintSession::reportActualWorkDurationInternal(AWorkDuration* wor
return 0;
}
+status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls,
+ int numSurfaceControls) {
+ if (!mSessionConfig.has_value()) {
+ return ENOTSUP;
+ }
+
+ std::vector<sp<IBinder>> layerHandles;
+ APerformanceHintManager::layersFromNativeSurfaces<sp<IBinder>>(windows, numWindows, controls,
+ numSurfaceControls,
+ layerHandles);
+
+ std::vector<ndk::SpAIBinder> ndkLayerHandles;
+ for (auto&& handle : layerHandles) {
+ ndkLayerHandles.emplace_back(ndk::SpAIBinder(AIBinder_fromPlatformBinder(handle)));
+ }
+
+ mHintSession->associateToLayers(ndkLayerHandles);
+ return 0;
+}
+
+template <class T>
+void APerformanceHintManager::layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls,
+ int numSurfaceControls,
+ std::vector<T>& out) {
+ std::scoped_lock lock(sHintMutex);
+ if (windows != nullptr) {
+ std::vector<ANativeWindow*> windowVec(windows, windows + numWindows);
+ for (auto&& window : windowVec) {
+ Surface* surface = static_cast<Surface*>(window);
+ if (Surface::isValid(surface)) {
+ const sp<IBinder>& handle = surface->getSurfaceControlHandle();
+ if (handle != nullptr) {
+ out.push_back(handle);
+ }
+ }
+ }
+ }
+
+ if (controls != nullptr) {
+ std::vector<ASurfaceControl*> controlVec(controls, controls + numSurfaceControls);
+ for (auto&& aSurfaceControl : controlVec) {
+ SurfaceControl* control = reinterpret_cast<SurfaceControl*>(aSurfaceControl);
+ if (control->isValid()) {
+ out.push_back(control->getHandle());
+ }
+ }
+ }
+}
+
// ===================================== FMQ wrapper implementation
bool FMQWrapper::isActive() {
@@ -963,8 +1048,7 @@ APerformanceHintSession* APerformanceHint_createSessionFromJava(
hal::SessionTag::APP, true);
}
-APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env,
- jobject sessionObj) {
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env, jobject sessionObj) {
VALIDATE_PTR(env)
VALIDATE_PTR(sessionObj)
return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
@@ -1065,6 +1149,14 @@ int APerformanceHint_notifyWorkloadReset(APerformanceHintSession* session, bool
return session->notifyWorkloadReset(cpu, gpu, debugName);
}
+int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session,
+ ANativeWindow** nativeWindows, int nativeWindowsSize,
+ ASurfaceControl** surfaceControls, int surfaceControlsSize) {
+ VALIDATE_PTR(session)
+ return session->setNativeSurfaces(nativeWindows, nativeWindowsSize, surfaceControls,
+ surfaceControlsSize);
+}
+
AWorkDuration* AWorkDuration_create() {
return new AWorkDuration();
}
@@ -1180,6 +1272,11 @@ int ASessionCreationConfig_setGraphicsPipeline(ASessionCreationConfig* config, b
config->modesToEnable.push_back(hal::SessionMode::GRAPHICS_PIPELINE);
} else {
std::erase(config->modesToEnable, hal::SessionMode::GRAPHICS_PIPELINE);
+
+ // Remove automatic timing modes if we turn off GRAPHICS_PIPELINE,
+ // as it is a strict pre-requisite for these to run
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
}
return 0;
}
@@ -1197,3 +1294,48 @@ void APerformanceHint_getRateLimiterPropertiesForTesting(int32_t* maxLoadHintsPe
void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) {
kForceNewHintBehavior = newBehavior;
}
+
+int ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config,
+ ANativeWindow** nativeWindows, int nativeWindowsSize,
+ ASurfaceControl** surfaceControls,
+ int surfaceControlsSize) {
+ VALIDATE_PTR(config)
+
+ APerformanceHintManager::layersFromNativeSurfaces<wp<IBinder>>(nativeWindows, nativeWindowsSize,
+ surfaceControls,
+ surfaceControlsSize,
+ config->layers);
+
+ if (config->layers.empty()) {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu,
+ bool gpu) {
+ VALIDATE_PTR(config)
+ if ((cpu || gpu) && !config->hasMode(hal::SessionMode::GRAPHICS_PIPELINE)) {
+ ALOGE("Automatic timing is not supported unless graphics pipeline mode is enabled first");
+ return ENOTSUP;
+ }
+
+ if (config->hasMode(hal::SessionMode::AUTO_CPU)) {
+ if (!cpu) {
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
+ }
+ } else if (cpu) {
+ config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_CPU));
+ }
+
+ if (config->hasMode(hal::SessionMode::AUTO_GPU)) {
+ if (!gpu) {
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
+ }
+ } else if (gpu) {
+ config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_GPU));
+ }
+
+ return 0;
+}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index fc64e9b48f6d..6bca1456db3a 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -794,28 +794,6 @@ void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* aSu
transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
}
-void ASurfaceTransaction_setFrameRateParams(
- ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
- float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
- ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) {
- CHECK_NOT_NULL(aSurfaceTransaction);
- CHECK_NOT_NULL(aSurfaceControl);
- Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
-
- if (desiredMaxRate < desiredMinRate) {
- ALOGW("desiredMaxRate must be greater than or equal to desiredMinRate");
- return;
- }
- // TODO(b/362798998): Fix plumbing to send modern params
- int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
- : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
- double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
- ? fixedSourceRate
- : desiredMinRate;
- transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
-}
-
void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl) {
CHECK_NOT_NULL(aSurfaceTransaction);
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b00658052bde..b8f574f44338 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -83,6 +83,7 @@ public:
(override));
MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
(override));
+ MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -99,6 +100,8 @@ public:
MOCK_METHOD(ScopedAStatus, close, (), (override));
MOCK_METHOD(ScopedAStatus, reportActualWorkDuration2,
(const ::std::vector<hal::WorkDuration>& workDurations), (override));
+ MOCK_METHOD(ScopedAStatus, associateToLayers,
+ (const std::vector<::ndk::SpAIBinder>& in_layerTokens), (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
diff --git a/nfc/tests/src/android/nfc/NdefRecordTest.java b/nfc/tests/src/android/nfc/NdefRecordTest.java
new file mode 100644
index 000000000000..231e939b4fbe
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NdefRecordTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NdefRecordTest {
+
+ @Test
+ public void testNdefRecordConstructor() throws FormatException {
+ NdefRecord applicationRecord = NdefRecord
+ .createApplicationRecord("com.android.test");
+ NdefRecord ndefRecord = new NdefRecord(applicationRecord.toByteArray());
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.toByteArray().length).isGreaterThan(0);
+ assertThat(ndefRecord.getType()).isEqualTo("android.com:pkg".getBytes());
+ assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes());
+ }
+
+ @Test
+ public void testCreateExternal() {
+ NdefRecord ndefRecord = NdefRecord.createExternal("test",
+ "android.com:pkg", "com.android.test".getBytes());
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.getType()).isEqualTo("test:android.com:pkg".getBytes());
+ assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes());
+ }
+
+ @Test
+ public void testCreateUri() {
+ NdefRecord ndefRecord = NdefRecord.createUri("http://www.example.com");
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN);
+ assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_URI);
+ }
+
+}
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
index 0cd0b3cb14f1..19818e0b06da 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
@@ -22,7 +22,7 @@
android:minWidth="@dimen/settingslib_expressive_space_medium3"
android:minHeight="@dimen/settingslib_expressive_space_medium3"
android:gravity="center"
- android:layout_marginEnd="-4dp"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall6"
android:filterTouchesWhenObscured="false">
<androidx.preference.internal.PreferenceImageView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
index 944bef6c9e09..c837ff43e46b 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
@@ -21,7 +21,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingVertical="@dimen/settingslib_expressive_space_small1"
- android:paddingHorizontal="@dimen/settingslib_expressive_space_small1"
+ android:paddingStart="@dimen/settingslib_expressive_space_none"
+ android:paddingEnd="@dimen/settingslib_expressive_space_small1"
android:filterTouchesWhenObscured="false">
<TextView
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
index 9764e64b8509..4428480eaa3e 100644
--- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
+++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt
@@ -75,6 +75,7 @@ open class TopIntroPreference @JvmOverloads constructor(
(holder.findViewById(R.id.collapsable_text_view) as? CollapsableTextView)?.apply {
setCollapsable(isCollapsable)
setMinLines(minLines)
+ visibility = if (title.isNullOrEmpty()) View.GONE else View.VISIBLE
setText(title.toString())
if (hyperlinkListener != null) {
setHyperlinkListener(hyperlinkListener)
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
index a7b91c2b2f6a..0f210e7e5e7b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
@@ -41,7 +41,7 @@
android:exported="true"
android:label="@string/accessibility_menu_settings_name"
android:launchMode="singleTop"
- android:theme="@style/SettingsTheme">
+ android:theme="@style/Theme.SettingsBase">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
index a138fa9be613..41691552f714 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
@@ -21,11 +21,6 @@
<item name="android:colorControlNormal">@color/colorControlNormal</item>
</style>
- <style name="SettingsTheme" parent="Theme.SettingsBase">
- <!-- Quick fix so that the preference page doesn't render under its parent header. -->
- <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
- </style>
-
<!--The basic theme for service and test case only-->
<style name="A11yMenuBaseTheme" parent="android:Theme.DeviceDefault.Light">
<item name="android:windowActionBar">false</item>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 129dd9b3c14d..3f7ce2c33007 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -21,15 +21,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.graphics.Insets;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.provider.Settings;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.widget.TextView;
import android.window.OnBackInvokedCallback;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
@@ -94,6 +97,18 @@ public class A11yMenuSettingsActivity extends FragmentActivity {
super.onViewCreated(view, savedInstanceState);
view.setLayoutDirection(
view.getResources().getConfiguration().getLayoutDirection());
+ view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+ @NonNull
+ @Override
+ public WindowInsets onApplyWindowInsets(@NonNull View v,
+ @NonNull WindowInsets windowInsets) {
+ Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars()
+ | WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.displayCutout());
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsets.CONSUMED;
+ }
+ });
}
/**
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 0f5e3679cc5f..ca2b9578f2be 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -16,6 +16,8 @@
package com.android.systemui.animation;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
@@ -39,6 +41,7 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.shared.TransitionUtil;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -90,7 +93,7 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
() -> {
mStartTransaction = t;
mFinishCallback = finishCallback;
- startAnimationInternal(info);
+ startAnimationInternal(info, /* states= */ null);
});
}
@@ -112,7 +115,13 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback,
WindowAnimationState[] states) {
- logD("takeOverAnimation - " + info);
+ logD("takeOverAnimation - info=" + info + ", states=" + Arrays.toString(states));
+ mHandler.post(
+ () -> {
+ mStartTransaction = t;
+ mFinishCallback = finishCallback;
+ startAnimationInternal(info, states);
+ });
}
@Override
@@ -121,14 +130,19 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
mHandler.post(this::cancel);
}
- private void startAnimationInternal(TransitionInfo info) {
+ private void startAnimationInternal(
+ TransitionInfo info, @Nullable WindowAnimationState[] states) {
if (!prepareUIs(info)) {
logE("Unable to prepare UI!");
finishAnimation(/* finished= */ false);
return;
}
// Notify player that we are starting.
- mPlayer.onStart(info, mStartTransaction, mOrigin, mOriginTransaction);
+ mPlayer.onStart(info, states, mStartTransaction, mOrigin, mOriginTransaction);
+
+ // Apply the initial transactions in case the player forgot to apply them.
+ mOriginTransaction.commit();
+ mStartTransaction.apply();
// Start the animator.
mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
@@ -205,7 +219,8 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
.setCornerRadius(leash, windowRadius)
.setWindowCrop(leash, bounds.width(), bounds.height());
}
- } else if (TransitionUtil.isClosingMode(mode)) {
+ } else if (TransitionUtil.isClosingMode(mode) || mode == TRANSIT_CHANGE) {
+ // TRANSIT_CHANGE refers to the closing window in predictive back animation.
closingSurfaces.add(change.getLeash());
// For closing surfaces, starting bounds are base bounds. Apply corner radius if
// it's full screen.
@@ -236,13 +251,8 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
// Attach origin UIComponent to origin leash.
mOriginTransaction = mOrigin.newTransaction();
- mOriginTransaction
- .attachToTransitionLeash(
- mOrigin, mOriginLeash, displayBounds.width(), displayBounds.height())
- .commit();
-
- // Apply all surface changes.
- mStartTransaction.apply();
+ mOriginTransaction.attachToTransitionLeash(
+ mOrigin, mOriginLeash, displayBounds.width(), displayBounds.height());
return true;
}
@@ -328,6 +338,58 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
/* baseBounds= */ maxBounds);
}
+ private static void applyWindowAnimationStates(
+ TransitionInfo info,
+ @Nullable WindowAnimationState[] states,
+ UIComponent closingApp,
+ UIComponent openingApp) {
+ if (states == null) {
+ // Nothing to apply.
+ return;
+ }
+ // Calculate bounds.
+ Rect maxClosingBounds = new Rect();
+ Rect maxOpeningBounds = new Rect();
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ Rect bound = getBounds(states[i]);
+ if (bound == null) {
+ continue;
+ }
+ int mode = info.getChanges().get(i).getMode();
+ if (TransitionUtil.isOpeningMode(mode)) {
+ maxOpeningBounds.union(bound);
+ } else if (TransitionUtil.isClosingMode(mode) || mode == TRANSIT_CHANGE) {
+ // TRANSIT_CHANGE refers to the closing window in predictive back animation.
+ maxClosingBounds.union(bound);
+ }
+ }
+
+ // Intentionally use a new transaction instead of reusing the existing transaction since we
+ // want to apply window animation states first without committing any other pending changes
+ // in the existing transaction. The existing transaction is expected to be committed by the
+ // onStart() client callback together with client's custom transformation.
+ UIComponent.Transaction transaction = closingApp.newTransaction();
+ if (!maxClosingBounds.isEmpty()) {
+ logD("Applying closing window bounds: " + maxClosingBounds);
+ transaction.setBounds(closingApp, maxClosingBounds);
+ }
+ if (!maxOpeningBounds.isEmpty()) {
+ logD("Applying opening window bounds: " + maxOpeningBounds);
+ transaction.setBounds(openingApp, maxOpeningBounds);
+ }
+ transaction.commit();
+ }
+
+ @Nullable
+ private static Rect getBounds(@Nullable WindowAnimationState state) {
+ if (state == null || state.bounds == null) {
+ return null;
+ }
+ Rect out = new Rect();
+ state.bounds.roundOut(out);
+ return out;
+ }
+
/**
* An interface that represents an origin transitions.
*
@@ -338,9 +400,14 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
/**
* Called when an origin transition starts. This method exposes the raw {@link
* TransitionInfo} so that clients can extract more information from it.
+ *
+ * <p>Note: if this transition is taking over a predictive back animation, the {@link
+ * WindowAnimationState} will be passed to this method. The concrete implementation is
+ * expected to apply the {@link WindowAnimationState} before continuing the transition.
*/
default void onStart(
TransitionInfo transitionInfo,
+ @Nullable WindowAnimationState[] states,
SurfaceControl.Transaction sfTransaction,
UIComponent origin,
UIComponent.Transaction uiTransaction) {
@@ -351,12 +418,15 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
.registerTransactionForClass(
SurfaceUIComponent.class,
new SurfaceUIComponent.Transaction(sfTransaction));
- // Wrap surfaces and start.
- onStart(
- transactions,
- origin,
- wrapSurfaces(transitionInfo, /* isOpening= */ false),
- wrapSurfaces(transitionInfo, /* isOpening= */ true));
+ // Wrap surfaces.
+ UIComponent closingApp = wrapSurfaces(transitionInfo, /* isOpening= */ false);
+ UIComponent openingApp = wrapSurfaces(transitionInfo, /* isOpening= */ true);
+
+ // Restore the pending animation states coming from predictive back transition.
+ applyWindowAnimationStates(transitionInfo, states, closingApp, openingApp);
+
+ // Start.
+ onStart(transactions, origin, closingApp, openingApp);
}
/**
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index 9cef43c3deba..cec740ad899b 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -89,7 +89,6 @@ public class ViewUIComponent implements UIComponent {
mSurfaceControl =
new SurfaceControl.Builder().setName("ViewUIComponent").setBufferSize(w, h).build();
mSurface = new Surface(mSurfaceControl);
- forceDraw();
// Attach surface to transition leash
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -99,7 +98,13 @@ public class ViewUIComponent implements UIComponent {
mView.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
// Make the view invisible AFTER the surface is shown.
- t.addTransactionCommittedListener(mView::post, () -> mView.setVisibility(View.INVISIBLE))
+ t.addTransactionCommittedListener(
+ mView::post,
+ () -> {
+ logD("Surface attached!");
+ forceDraw();
+ mView.setVisibility(View.INVISIBLE);
+ })
.apply();
}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
index 3cbb688ce7ca..6b26ac5f4692 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
@@ -16,8 +16,10 @@
package com.android.systemui.animation.server;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -26,6 +28,7 @@ import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -51,8 +54,8 @@ import java.util.function.Predicate;
/** An implementation of the {@link IOriginTransitions}. */
public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
- private static final boolean DEBUG = true;
private static final String TAG = "OriginTransitions";
+ private static final boolean DEBUG = Build.IS_USERDEBUG || Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
private final ShellTransitions mShellTransitions;
@@ -149,18 +152,7 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
if (DEBUG) {
Log.d(TAG, "startAnimation: " + info);
}
- if (!mOnStarting.test(info)) {
- Log.w(TAG, "Skipping cancelled transition " + mTransition);
- t.addTransactionCommittedListener(
- mExecutor,
- () -> {
- try {
- finishCallback.onTransitionFinished(null, null);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to report finish.", e);
- }
- })
- .apply();
+ if (maybeInterceptTransition(info, t, finishCallback)) {
return;
}
mTransition.startAnimation(token, info, t, finishCallback);
@@ -191,6 +183,9 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
if (DEBUG) {
Log.d(TAG, "takeOverAnimation: " + info);
}
+ if (maybeInterceptTransition(info, t, finishCallback)) {
+ return;
+ }
mTransition.takeOverAnimation(transition, info, t, finishCallback, states);
}
@@ -207,6 +202,27 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
public String toString() {
return "RemoteTransitionDelegate{transition=" + mTransition + "}";
}
+
+ private boolean maybeInterceptTransition(
+ TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishCallback) {
+ if (!mOnStarting.test(info)) {
+ Log.w(TAG, "Intercepting cancelled transition " + mTransition);
+ t.addTransactionCommittedListener(
+ mExecutor,
+ () -> {
+ try {
+ finishCallback.onTransitionFinished(null, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to report finish.", e);
+ }
+ })
+ .apply();
+ return true;
+ }
+ return false;
+ }
}
/** A data record containing the origin transition pieces. */
@@ -229,13 +245,25 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
if (mDestroyed) {
return false;
}
- TransitionFilter filter = createFilterForReverseTransition(info);
+ TransitionFilter filter =
+ createFilterForReverseTransition(
+ info, /* forPredictiveBackTakeover= */ false);
if (filter != null) {
if (DEBUG) {
Log.d(TAG, "Registering filter " + filter);
}
mShellTransitions.registerRemote(filter, mWrappedReturnTransition);
}
+ TransitionFilter takeoverFilter =
+ createFilterForReverseTransition(
+ info, /* forPredictiveBackTakeover= */ true);
+ if (takeoverFilter != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Registering filter for takeover " + takeoverFilter);
+ }
+ mShellTransitions.registerRemoteForTakeover(
+ takeoverFilter, mWrappedReturnTransition);
+ }
return true;
}
}
@@ -331,7 +359,8 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
}
@Nullable
- private static TransitionFilter createFilterForReverseTransition(TransitionInfo info) {
+ private static TransitionFilter createFilterForReverseTransition(
+ TransitionInfo info, boolean forPredictiveBackTakeover) {
TaskInfo launchingTaskInfo = null;
TaskInfo launchedTaskInfo = null;
ComponentName launchingActivity = null;
@@ -365,7 +394,9 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
if (DEBUG) {
Log.d(
TAG,
- "createFilterForReverseTransition: launchingTaskInfo="
+ "createFilterForReverseTransition: forPredictiveBackTakeover="
+ + forPredictiveBackTakeover
+ + ", launchingTaskInfo="
+ launchingTaskInfo
+ ", launchedTaskInfo="
+ launchedTaskInfo
@@ -395,8 +426,20 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
+ " cookie!");
return null;
}
+ if (forPredictiveBackTakeover && launchedTaskInfo == null) {
+ // Predictive back take over currently only support cross-task transition.
+ Log.d(
+ TAG,
+ "createFilterForReverseTransition: skipped - unable to find launched task"
+ + " for predictive back takeover");
+ return null;
+ }
TransitionFilter filter = new TransitionFilter();
- filter.mTypeSet = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ if (forPredictiveBackTakeover) {
+ filter.mTypeSet = new int[] {TRANSIT_PREPARE_BACK_NAVIGATION};
+ } else {
+ filter.mTypeSet = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ }
// The opening activity of the return transition must match the activity we just closed.
TransitionFilter.Requirement req1 = new TransitionFilter.Requirement();
@@ -405,15 +448,18 @@ public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
launchingActivity == null ? launchingTaskInfo.topActivity : launchingActivity;
TransitionFilter.Requirement req2 = new TransitionFilter.Requirement();
- req2.mModes = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ if (forPredictiveBackTakeover) {
+ req2.mModes = new int[] {TRANSIT_CHANGE};
+ } else {
+ req2.mModes = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ }
if (launchedTaskInfo != null) {
// For task transitions, the closing task's cookie must match the task we just
// launched.
req2.mLaunchCookie = launchedTaskInfo.launchCookies.get(0);
} else {
// For activity transitions, the closing activity of the return transition must
- // match
- // the activity we just launched.
+ // match the activity we just launched.
req2.mTopActivity = launchedActivity;
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 38f09988e7a7..3eeaf41d874a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -424,15 +424,16 @@ constructor(
newKeyguardOccludedState: Boolean?
) {
super.onTransitionAnimationCancelled(newKeyguardOccludedState)
- cleanUp()
+ onDispose()
}
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
super.onTransitionAnimationEnd(isExpandingFullyAbove)
- cleanUp()
+ onDispose()
}
- private fun cleanUp() {
+ override fun onDispose() {
+ super.onDispose()
cleanUpRunnable?.run()
}
}
@@ -560,6 +561,7 @@ constructor(
cookie: TransitionCookie? = null,
component: ComponentName? = null,
returnCujType: Int? = null,
+ isEphemeral: Boolean = true,
): Controller? {
// Make sure the View we launch from implements LaunchableView to avoid visibility
// issues.
@@ -587,6 +589,7 @@ constructor(
cookie,
component,
returnCujType,
+ isEphemeral,
)
}
}
@@ -647,6 +650,9 @@ constructor(
* appropriately.
*/
fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
+
+ /** The controller will not be used again. Clean up the relevant internal state. */
+ fun onDispose() {}
}
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
index 3ba9a2974846..b56a68cb2dd6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
@@ -39,7 +39,8 @@ interface Expandable {
launchCujType: Int? = null,
cookie: ActivityTransitionAnimator.TransitionCookie? = null,
component: ComponentName? = null,
- returnCujType: Int? = null
+ returnCujType: Int? = null,
+ isEphemeral: Boolean = true,
): ActivityTransitionAnimator.Controller?
/**
@@ -55,7 +56,8 @@ interface Expandable {
launchCujType,
cookie = null,
component = null,
- returnCujType = null
+ returnCujType = null,
+ isEphemeral = true,
)
}
@@ -80,14 +82,16 @@ interface Expandable {
launchCujType: Int?,
cookie: ActivityTransitionAnimator.TransitionCookie?,
component: ComponentName?,
- returnCujType: Int?
+ returnCujType: Int?,
+ isEphemeral: Boolean,
): ActivityTransitionAnimator.Controller? {
return ActivityTransitionAnimator.Controller.fromView(
view,
launchCujType,
cookie,
component,
- returnCujType
+ returnCujType,
+ isEphemeral,
)
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index e626c04675e1..558c1eba2c1c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -67,6 +67,12 @@ constructor(
/** The [CujType] associated to this return animation. */
private val returnCujType: Int? = null,
+
+ /**
+ * Whether this controller should be invalidated after its first use, and whenever [ghostedView]
+ * is detached.
+ */
+ private val isEphemeral: Boolean = false,
private var interactionJankMonitor: InteractionJankMonitor =
InteractionJankMonitor.getInstance(),
) : ActivityTransitionAnimator.Controller {
@@ -119,6 +125,19 @@ constructor(
returnCujType
}
+ /**
+ * Used to automatically clean up the internal state once [ghostedView] is detached from the
+ * hierarchy.
+ */
+ private val detachListener =
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {}
+
+ override fun onViewDetachedFromWindow(v: View) {
+ onDispose()
+ }
+ }
+
init {
// Make sure the View we launch from implements LaunchableView to avoid visibility issues.
if (ghostedView !is LaunchableView) {
@@ -155,6 +174,16 @@ constructor(
}
background = findBackground(ghostedView)
+
+ if (TransitionAnimator.returnAnimationsEnabled() && isEphemeral) {
+ ghostedView.addOnAttachStateChangeListener(detachListener)
+ }
+ }
+
+ override fun onDispose() {
+ if (TransitionAnimator.returnAnimationsEnabled()) {
+ ghostedView.removeOnAttachStateChangeListener(detachListener)
+ }
}
/**
@@ -164,7 +193,7 @@ constructor(
protected open fun setBackgroundCornerRadius(
background: Drawable,
topCornerRadius: Float,
- bottomCornerRadius: Float
+ bottomCornerRadius: Float,
) {
// By default, we rely on WrappedDrawable to set/restore the background radii before/after
// each draw.
@@ -195,7 +224,7 @@ constructor(
val state =
TransitionAnimator.State(
topCornerRadius = getCurrentTopCornerRadius(),
- bottomCornerRadius = getCurrentBottomCornerRadius()
+ bottomCornerRadius = getCurrentBottomCornerRadius(),
)
fillGhostedViewState(state)
return state
@@ -269,7 +298,7 @@ constructor(
override fun onTransitionAnimationProgress(
state: TransitionAnimator.State,
progress: Float,
- linearProgress: Float
+ linearProgress: Float,
) {
val ghostView = this.ghostView ?: return
val backgroundView = this.backgroundView!!
@@ -317,11 +346,11 @@ constructor(
scale,
scale,
ghostedViewState.centerX - transitionContainerLocation[0],
- ghostedViewState.centerY - transitionContainerLocation[1]
+ ghostedViewState.centerY - transitionContainerLocation[1],
)
ghostViewMatrix.postTranslate(
(leftChange + rightChange) / 2f,
- (topChange + bottomChange) / 2f
+ (topChange + bottomChange) / 2f,
)
ghostView.animationMatrix = ghostViewMatrix
@@ -462,7 +491,7 @@ constructor(
private fun updateRadii(
radii: FloatArray,
topCornerRadius: Float,
- bottomCornerRadius: Float
+ bottomCornerRadius: Float,
) {
radii[0] = topCornerRadius
radii[1] = topCornerRadius
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index cbe11a3f2f60..8a57e8cbbb20 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.internal.util.Preconditions.checkArgument;
@@ -163,7 +164,8 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
t.show(wallpapers[i].leash);
t.setAlpha(wallpapers[i].leash, 1.f);
}
- if (ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue()) {
+ if (ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue()
+ || ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue()) {
resetLauncherAlphaOnDesktopExit(info, launcherTask, leashMap, t);
}
} else {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index a55df2b36a80..103a9b5cf5f4 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -52,6 +52,9 @@ import kotlin.math.roundToInt
interface ExpandableController {
/** The [Expandable] controlled by this controller. */
val expandable: Expandable
+
+ /** Called when the [Expandable] stop being included in the composition. */
+ fun onDispose()
}
/**
@@ -88,33 +91,44 @@ fun rememberExpandableController(
// Whether this composable is still composed. We only do the dialog exit animation if this is
// true.
val isComposed = remember { mutableStateOf(true) }
- DisposableEffect(Unit) { onDispose { isComposed.value = false } }
-
- return remember(
- color,
- contentColor,
- shape,
- borderStroke,
- composeViewRoot,
- density,
- layoutDirection,
- ) {
- ExpandableControllerImpl(
+
+ val controller =
+ remember(
color,
contentColor,
shape,
borderStroke,
composeViewRoot,
density,
- animatorState,
- isDialogShowing,
- overlay,
- currentComposeViewInOverlay,
- boundsInComposeViewRoot,
layoutDirection,
- isComposed,
- )
+ ) {
+ ExpandableControllerImpl(
+ color,
+ contentColor,
+ shape,
+ borderStroke,
+ composeViewRoot,
+ density,
+ animatorState,
+ isDialogShowing,
+ overlay,
+ currentComposeViewInOverlay,
+ boundsInComposeViewRoot,
+ layoutDirection,
+ isComposed,
+ )
+ }
+
+ DisposableEffect(Unit) {
+ onDispose {
+ isComposed.value = false
+ if (TransitionAnimator.returnAnimationsEnabled()) {
+ controller.onDispose()
+ }
+ }
}
+
+ return controller
}
internal class ExpandableControllerImpl(
@@ -132,19 +146,29 @@ internal class ExpandableControllerImpl(
private val layoutDirection: LayoutDirection,
private val isComposed: State<Boolean>,
) : ExpandableController {
+ /** The [ActivityTransitionAnimator.Controller] to be cleaned up [onDispose]. */
+ private var activityControllerForDisposal: ActivityTransitionAnimator.Controller? = null
+
override val expandable: Expandable =
object : Expandable {
override fun activityTransitionController(
launchCujType: Int?,
cookie: ActivityTransitionAnimator.TransitionCookie?,
component: ComponentName?,
- returnCujType: Int?
+ returnCujType: Int?,
+ isEphemeral: Boolean,
): ActivityTransitionAnimator.Controller? {
if (!isComposed.value) {
return null
}
- return activityController(launchCujType, cookie, component, returnCujType)
+ val controller = activityController(launchCujType, cookie, component, returnCujType)
+ if (TransitionAnimator.returnAnimationsEnabled() && isEphemeral) {
+ activityControllerForDisposal?.onDispose()
+ activityControllerForDisposal = controller
+ }
+
+ return controller
}
override fun dialogTransitionController(
@@ -158,6 +182,11 @@ internal class ExpandableControllerImpl(
}
}
+ override fun onDispose() {
+ activityControllerForDisposal?.onDispose()
+ activityControllerForDisposal = null
+ }
+
/**
* Create a [TransitionAnimator.Controller] that is going to be used to drive an activity or
* dialog animation. This controller will:
@@ -181,7 +210,7 @@ internal class ExpandableControllerImpl(
override fun onTransitionAnimationProgress(
state: TransitionAnimator.State,
progress: Float,
- linearProgress: Float
+ linearProgress: Float,
) {
// We copy state given that it's always the same object that is mutated by
// ActivityTransitionAnimator.
@@ -269,7 +298,7 @@ internal class ExpandableControllerImpl(
launchCujType: Int?,
cookie: ActivityTransitionAnimator.TransitionCookie?,
component: ComponentName?,
- returnCujType: Int?
+ returnCujType: Int?,
): ActivityTransitionAnimator.Controller {
val delegate = transitionController()
return object :
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 4bccac1e3ba0..86c5fd824d8f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -28,7 +28,6 @@ import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastForEach
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.transformation.SharedElementTransformation
-import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
@@ -528,39 +527,6 @@ internal class MutableSceneTransitionLayoutStateImpl(
transitionStates = listOf(TransitionState.Idle(scene, currentOverlays))
}
- /**
- * Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap
- * to the closest scene.
- *
- * Important: Snapping to the closest scene will instantly finish *all* ongoing transitions,
- * only the progress of the last transition will be checked.
- *
- * @return true if snapped to the closest scene.
- */
- internal fun snapToIdleIfClose(threshold: Float): Boolean {
- val transition = currentTransition ?: return false
- val progress = transition.progress
-
- fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
-
- fun finishAllTransitions() {
- // Force finish all transitions.
- while (currentTransitions.isNotEmpty()) {
- finishTransition(transitionStates[0] as TransitionState.Transition)
- }
- }
-
- val shouldSnap =
- (isProgressCloseTo(0f) && transition.isFromCurrentContent()) ||
- (isProgressCloseTo(1f) && transition.isToCurrentContent())
- return if (shouldSnap) {
- finishAllTransitions()
- true
- } else {
- false
- }
- }
-
override fun showOverlay(
overlay: OverlayKey,
animationScope: CoroutineScope,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index 79ca891babd1..3b7d661ba91a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -18,12 +18,9 @@ package com.android.compose.animation.scene
import android.util.Log
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.animation.scene.TestOverlays.OverlayA
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
import com.android.compose.animation.scene.TestScenes.SceneC
@@ -169,130 +166,6 @@ class SceneTransitionLayoutStateTest {
assertThat(state.currentTransition?.transformationSpec?.transformationMatchers).hasSize(2)
}
- @Test
- fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransitionImmediately(
- animationScope = backgroundScope,
- transition(from = SceneA, to = SceneB, current = { SceneA }, progress = { 0.2f }),
- )
- assertThat(state.isTransitioning()).isTrue()
-
- // Ignore the request if the progress is not close to 0 or 1, using the threshold.
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.isTransitioning()).isTrue()
-
- // Go to the initial scene if it is close to 0.
- assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
- assertThat(state.isTransitioning()).isFalse()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
- }
-
- @Test
- fun snapToIdleIfClose_snapToStart_overlays() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransitionImmediately(
- animationScope = backgroundScope,
- transition(SceneA, OverlayA, isEffectivelyShown = { false }, progress = { 0.2f }),
- )
- assertThat(state.isTransitioning()).isTrue()
-
- // Ignore the request if the progress is not close to 0 or 1, using the threshold.
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.isTransitioning()).isTrue()
-
- // Go to the initial scene if it is close to 0.
- assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
- assertThat(state.isTransitioning()).isFalse()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
- }
-
- @Test
- fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransitionImmediately(
- animationScope = backgroundScope,
- transition(from = SceneA, to = SceneB, progress = { 0.8f }),
- )
- assertThat(state.isTransitioning()).isTrue()
-
- // Ignore the request if the progress is not close to 0 or 1, using the threshold.
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.isTransitioning()).isTrue()
-
- // Go to the final scene if it is close to 1.
- assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
- assertThat(state.isTransitioning()).isFalse()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
- }
-
- @Test
- fun snapToIdleIfClose_snapToEnd_overlays() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransitionImmediately(
- animationScope = backgroundScope,
- transition(SceneA, OverlayA, isEffectivelyShown = { true }, progress = { 0.8f }),
- )
- assertThat(state.isTransitioning()).isTrue()
-
- // Ignore the request if the progress is not close to 0 or 1, using the threshold.
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.isTransitioning()).isTrue()
-
- // Go to the final scene if it is close to 1.
- assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
- assertThat(state.isTransitioning()).isFalse()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA, setOf(OverlayA)))
- }
-
- @Test
- fun snapToIdleIfClose_multipleTransitions() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-
- val aToB = transition(from = SceneA, to = SceneB, progress = { 0.5f })
- state.startTransitionImmediately(animationScope = backgroundScope, aToB)
- assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
-
- val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f })
- state.startTransitionImmediately(animationScope = backgroundScope, bToC)
- assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
-
- // Ignore the request if the progress is not close to 0 or 1, using the threshold.
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
-
- // Go to the final scene if it is close to 1.
- assertThat(state.snapToIdleIfClose(threshold = 0.2f)).isTrue()
- assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC))
- assertThat(state.currentTransitions).isEmpty()
- }
-
- @Test
- fun snapToIdleIfClose_closeButNotCurrentScene() = runMonotonicClockTest {
- val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- var progress by mutableStateOf(0f)
- var currentScene by mutableStateOf(SceneB)
- state.startTransitionImmediately(
- animationScope = backgroundScope,
- transition(
- from = SceneA,
- to = SceneB,
- current = { currentScene },
- progress = { progress },
- ),
- )
- assertThat(state.isTransitioning()).isTrue()
-
- // Ignore the request if we are close to a scene that is not the current scene
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.isTransitioning()).isTrue()
-
- progress = 1f
- currentScene = SceneA
- assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
- assertThat(state.isTransitioning()).isTrue()
- }
-
private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB(
progress: () -> Float,
sceneTransitions: SceneTransitions,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index 80de087971c5..fa8cdcc4ce2b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -24,7 +24,6 @@ import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -40,7 +39,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -56,15 +54,11 @@ public class DragToInteractAnimationControllerTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mockSecureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mockSecureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 24f3a29e64ee..7e4b6f913770 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -16,16 +16,11 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import android.content.Context;
import android.content.res.Configuration;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -33,7 +28,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.settings.SecureSettings;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -42,8 +36,6 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
/** Tests for {@link MenuInfoRepository}. */
@@ -54,30 +46,16 @@ public class MenuInfoRepositoryTest extends SysuiTestCase {
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
- private AccessibilityManager mAccessibilityManager;
-
- @Mock
private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;
@Mock
private SecureSettings mSecureSettings;
private MenuInfoRepository mMenuInfoRepository;
- private final List<String> mShortcutTargets = new ArrayList<>();
@Before
public void setUp() {
- mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
- mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
- doReturn(mShortcutTargets).when(mAccessibilityManager).getAccessibilityShortcutTargets(
- anyInt());
-
- mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager,
- mMockSettingsContentsChanged, mSecureSettings);
- }
-
- @After
- public void tearDown() {
- mShortcutTargets.clear();
+ mMenuInfoRepository = new MenuInfoRepository(mContext, mMockSettingsContentsChanged,
+ mSecureSettings);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 157cccc3d62f..1f48bec97b2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -83,8 +83,7 @@ public class MenuItemAccessibilityDelegateTest extends SysuiTestCase {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mSecureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mSecureSettings);
final int halfScreenHeight =
stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 46f076a75116..f7b81cc49f0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -33,7 +33,6 @@ import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.recyclerview.widget.RecyclerView;
@@ -53,7 +52,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -80,15 +78,11 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
@Before
public void setUp() throws Exception {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
windowManager);
mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index ee8ce17cecd4..c1708d175224 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -32,7 +32,6 @@ import android.graphics.drawable.GradientDrawable;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -49,7 +48,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -68,9 +66,6 @@ public class MenuViewTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
private SysuiTestableContext mSpyContext;
@Before
@@ -89,8 +84,7 @@ public class MenuViewTest extends SysuiTestCase {
doNothing().when(mSpyContext).startActivity(any());
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index a8048793be06..f924ccb42cb0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -62,6 +62,7 @@ import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.complication.ComplicationLayoutEngine
import com.android.systemui.complication.dagger.ComplicationComponent
import com.android.systemui.dreams.complication.HideComplicationTouchHandler
+import com.android.systemui.dreams.complication.dagger.DreamComplicationComponent
import com.android.systemui.dreams.dagger.DreamOverlayComponent
import com.android.systemui.dreams.touch.CommunalTouchHandler
import com.android.systemui.flags.andSceneContainer
@@ -119,8 +120,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val mComplicationComponentFactory = mock<ComplicationComponent.Factory>()
private val mComplicationHostViewController = mock<ComplicationHostViewController>()
private val mComplicationVisibilityController = mock<ComplicationLayoutEngine>()
- private val mDreamComplicationComponentFactory =
- mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory>()
+ private val mDreamComplicationComponentFactory = mock<DreamComplicationComponent.Factory>()
private val mHideComplicationTouchHandler = mock<HideComplicationTouchHandler>()
private val mDreamOverlayComponentFactory = mock<DreamOverlayComponent.Factory>()
private val mCommunalTouchHandler = mock<CommunalTouchHandler>()
@@ -160,8 +160,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
private lateinit var mService: DreamOverlayService
private class EnvironmentComponents(
- val dreamsComplicationComponent:
- com.android.systemui.dreams.complication.dagger.ComplicationComponent,
+ val dreamsComplicationComponent: DreamComplicationComponent,
val dreamOverlayComponent: DreamOverlayComponent,
val complicationComponent: ComplicationComponent,
val ambientTouchComponent: AmbientTouchComponent,
@@ -186,8 +185,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
}
private fun setupComponentFactories(
- dreamComplicationComponentFactory:
- com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory,
+ dreamComplicationComponentFactory: DreamComplicationComponent.Factory,
dreamOverlayComponentFactory: DreamOverlayComponent.Factory,
complicationComponentFactory: ComplicationComponent.Factory,
ambientTouchComponentFactory: AmbientTouchComponent.Factory,
@@ -208,8 +206,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
whenever(complicationComponent.getVisibilityController())
.thenReturn(mComplicationVisibilityController)
- val dreamComplicationComponent =
- mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent>()
+ val dreamComplicationComponent = mock<DreamComplicationComponent>()
whenever(dreamComplicationComponent.getHideComplicationTouchHandler())
.thenReturn(mHideComplicationTouchHandler)
whenever(dreamOverlayComponent.communalTouchHandler).thenReturn(mCommunalTouchHandler)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index 72916a35814f..8740c7152620 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -36,14 +36,14 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.ALL_SUPPORTED_MODIFIERS
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutCategory
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardKeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
@@ -323,4 +323,33 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() {
}
}
}
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun categories_isUpdatedAfterCustomShortcutsAreReset() {
+ testScope.runTest {
+ // TODO(b/380445594) refactor tests and move these stubbings to ShortcutHelperTestHelper
+ var customInputGestures = listOf(allAppsInputGestureData)
+ whenever(inputManager.getCustomInputGestures(anyOrNull())).then {
+ return@then customInputGestures
+ }
+ whenever(
+ inputManager.removeAllCustomInputGestures(
+ /* filter = */ InputGestureData.Filter.KEY
+ )
+ )
+ .then {
+ customInputGestures = emptyList()
+ return@then CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+ }
+
+ val categories by collectLastValue(repo.categories)
+ helper.toggle(deviceId = 123)
+ repo.onCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+
+ assertThat(categories).containsExactly(allAppsShortcutCategory)
+ repo.resetAllCustomShortcuts()
+ assertThat(categories).isEmpty()
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 7855d4219788..3faba95a97b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -49,7 +49,6 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
-import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.res.R
object TestShortcuts {
@@ -626,9 +625,6 @@ object TestShortcuts {
}
}
- val expectedStandardDeleteShortcutUiState =
- ShortcutCustomizationUiState.DeleteShortcutDialog(isDialogShowing = false)
-
val keyDownEventWithoutActionKeyPressed =
androidx.compose.ui.input.key.KeyEvent(
android.view.KeyEvent(
@@ -671,12 +667,4 @@ object TestShortcuts {
categoryType = ShortcutCategoryType.System,
subCategoryLabel = "Standard subcategory",
)
-
- val expectedStandardAddShortcutUiState =
- ShortcutCustomizationUiState.AddShortcutDialog(
- shortcutLabel = "Standard shortcut",
- defaultCustomShortcutModifierKey =
- ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta),
- isDialogShowing = false,
- )
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index d3d1a3506725..2d05ee0fe234 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -28,25 +28,26 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedStandardAddShortcutUiState
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedStandardDeleteShortcutUiState
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithActionKeyPressed
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithoutActionKeyPressed
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyUpEventWithActionKeyPressed
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddShortcutRequest
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shortcutCustomizationViewModelFactory
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -62,8 +63,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
private val mockUserContext: Context = mock()
private val kosmos =
- Kosmos().also {
- it.testCase = this
+ testKosmos().also {
it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
}
private val testScope = kosmos.testScope
@@ -92,7 +92,23 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- assertThat(uiState).isEqualTo(expectedStandardAddShortcutUiState)
+ assertThat(uiState).isEqualTo(
+ AddShortcutDialog(
+ shortcutLabel = "Standard shortcut",
+ defaultCustomShortcutModifierKey =
+ ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta),
+ )
+ )
+ }
+ }
+
+ @Test
+ fun uiState_correctlyUpdatedWhenResetShortcutCustomizationIsRequested() {
+ testScope.runTest {
+ viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+ val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+
+ assertThat(uiState).isEqualTo(ResetShortcutDialog())
}
}
@@ -102,7 +118,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- assertThat(uiState).isEqualTo(expectedStandardDeleteShortcutUiState)
+ assertThat(uiState).isEqualTo(DeleteShortcutDialog())
}
}
@@ -113,7 +129,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
viewModel.onDialogShown()
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).isDialogShowing)
+ assertThat((uiState as AddShortcutDialog).isDialogShowing)
.isTrue()
}
}
@@ -126,13 +142,25 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onDialogShown()
assertThat(
- (uiState as ShortcutCustomizationUiState.DeleteShortcutDialog).isDialogShowing
+ (uiState as DeleteShortcutDialog).isDialogShowing
)
.isTrue()
}
}
@Test
+ fun uiState_consumedOnResetDialogShown() {
+ testScope.runTest {
+ val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+ viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+ viewModel.onDialogShown()
+
+ assertThat((uiState as ResetShortcutDialog).isDialogShowing)
+ .isTrue()
+ }
+ }
+
+ @Test
fun uiState_inactiveAfterDialogIsDismissed() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
@@ -148,7 +176,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).pressedKeys)
+ assertThat((uiState as AddShortcutDialog).pressedKeys)
.isEmpty()
}
}
@@ -173,7 +201,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
viewModel.onDialogShown()
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).errorMessage)
+ assertThat((uiState as AddShortcutDialog).errorMessage)
.isEmpty()
}
}
@@ -187,7 +215,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
openAddShortcutDialogAndSetShortcut()
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).errorMessage)
+ assertThat((uiState as AddShortcutDialog).errorMessage)
.isEqualTo(
context.getString(
R.string.shortcut_customizer_key_combination_in_use_error_message
@@ -205,7 +233,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
openAddShortcutDialogAndSetShortcut()
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).errorMessage)
+ assertThat((uiState as AddShortcutDialog).errorMessage)
.isEqualTo(
context.getString(
R.string.shortcut_customizer_key_combination_in_use_error_message
@@ -223,7 +251,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
openAddShortcutDialogAndSetShortcut()
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).errorMessage)
+ assertThat((uiState as AddShortcutDialog).errorMessage)
.isEqualTo(context.getString(R.string.shortcut_customizer_generic_error_message))
}
}
@@ -244,6 +272,18 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
}
@Test
+ fun uiState_becomesInactiveAfterSuccessfullyResettingShortcuts() {
+ testScope.runTest {
+ val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
+ whenever(inputManager.getCustomInputGestures(any())).thenReturn(emptyList())
+
+ openResetShortcutDialogAndResetAllCustomShortcuts()
+
+ assertThat(uiState).isEqualTo(ShortcutCustomizationUiState.Inactive)
+ }
+ }
+
+ @Test
fun onKeyPressed_handlesKeyEvents_whereActionKeyIsAlsoPressed() {
testScope.runTest {
viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
@@ -272,7 +312,7 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
// Note that Action Key is excluded as it's already displayed on the UI
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).pressedKeys)
+ assertThat((uiState as AddShortcutDialog).pressedKeys)
.containsExactly(ShortcutKey.Text("Ctrl"), ShortcutKey.Text("A"))
}
}
@@ -286,13 +326,13 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
// Note that Action Key is excluded as it's already displayed on the UI
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).pressedKeys)
+ assertThat((uiState as AddShortcutDialog).pressedKeys)
.containsExactly(ShortcutKey.Text("Ctrl"), ShortcutKey.Text("A"))
// Close the dialog and show it again
viewModel.onDialogDismissed()
viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
- assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).pressedKeys)
+ assertThat((uiState as AddShortcutDialog).pressedKeys)
.isEmpty()
}
}
@@ -313,4 +353,11 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() {
viewModel.deleteShortcutCurrentlyBeingCustomized()
}
+
+ private suspend fun openResetShortcutDialogAndResetAllCustomShortcuts() {
+ viewModel.onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+ viewModel.onDialogShown()
+
+ viewModel.resetAllCustomShortcuts()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 614d51e7ac99..093ef46569d1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -41,6 +41,7 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.flag.junit.FlagsParameterization;
+import android.provider.Settings;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.WindowManager;
@@ -70,6 +71,7 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.settings.FakeSettings;
import com.google.common.util.concurrent.MoreExecutors;
@@ -111,6 +113,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
+ private FakeSettings mSecureSettings;
private final Executor mMainExecutor = MoreExecutors.directExecutor();
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -131,6 +134,10 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ mSecureSettings = new FakeSettings();
+ mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 0);
+
// Preferred refresh rate is equal to the first displayMode's refresh rate
mPreferredRefreshRate = mContext.getDisplay().getSystemSupportedModes()[0].getRefreshRate();
overrideResource(
@@ -164,12 +171,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
() -> mSelectedUserInteractor,
mUserTracker,
mKosmos.getNotificationShadeWindowModel(),
- mKosmos::getCommunalInteractor) {
- @Override
- protected boolean isDebuggable() {
- return false;
- }
- };
+ mSecureSettings,
+ mKosmos::getCommunalInteractor);
mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {});
mNotificationShadeWindowController.fetchWindowRootView();
@@ -351,6 +354,19 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
}
@Test
+ public void setKeyguardShowingWithSecureWindowsDisabled_disablesSecureFlag() {
+ mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 1);
+ mNotificationShadeWindowController.setBouncerShowing(true);
+
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue();
+ assertThat(
+ (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_PRIVACY)
+ != 0)
+ .isTrue();
+ }
+
+ @Test
public void setKeyguardNotShowing_disablesSecureFlag() {
mNotificationShadeWindowController.setBouncerShowing(false);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
new file mode 100644
index 000000000000..ef1ae093bcc9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.display
+
+import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.display.data.repository.display
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val displayRepository = kosmos.displayRepository
+ val underTest = StatusBarTouchShadeDisplayPolicy(displayRepository, testScope.backgroundScope)
+
+ @Test
+ fun displayId_defaultToDefaultDisplay() {
+ assertThat(underTest.displayId.value).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun onStatusBarTouched_called_updatesDisplayId() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ underTest.onStatusBarTouched(2)
+
+ assertThat(displayId).isEqualTo(2)
+ }
+
+ @Test
+ fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() =
+ testScope.runTest {
+ val displayIds by collectValues(underTest.displayId)
+ assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
+
+ underTest.onStatusBarTouched(2)
+
+ // Never set, as 2 was not a display according to the repository.
+ assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
+ }
+
+ @Test
+ fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ underTest.onStatusBarTouched(2)
+
+ assertThat(displayId).isEqualTo(2)
+
+ displayRepository.removeDisplay(2)
+
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index f9f2cd328094..3bd12e6efab6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
@@ -35,7 +36,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeHeadsUpTracker;
@@ -55,7 +55,6 @@ import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,7 +86,6 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private KeyguardStateController mKeyguardStateController;
private CommandQueue mCommandQueue;
private NotificationRoundnessManager mNotificationRoundnessManager;
- private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
@Before
public void setUp() throws Exception {
@@ -124,7 +122,6 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mNotificationRoundnessManager,
mHeadsUpStatusBarView,
new Clock(mContext, null),
- mFeatureFlags,
mock(HeadsUpNotificationIconInteractor.class),
Optional.of(mOperatorNameView));
mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
@@ -141,21 +138,21 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mRow.setPinnedStatus(PinnedStatus.NotPinned);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
+ assertNull(mHeadsUpStatusBarView.getShowingEntry());
}
@Test
- public void testShownUpdated() {
+ public void testPinnedStatusUpdated() {
mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
- assertTrue(mHeadsUpAppearanceController.isShown());
+ assertEquals(PinnedStatus.PinnedBySystem, mHeadsUpAppearanceController.getPinnedStatus());
mRow.setPinnedStatus(PinnedStatus.NotPinned);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- Assert.assertFalse(mHeadsUpAppearanceController.isShown());
+ assertEquals(PinnedStatus.NotPinned, mHeadsUpAppearanceController.getPinnedStatus());
}
@Test
@@ -210,7 +207,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mNotificationRoundnessManager,
mHeadsUpStatusBarView,
new Clock(mContext, null),
- mFeatureFlags, mock(HeadsUpNotificationIconInteractor.class),
+ mock(HeadsUpNotificationIconInteractor.class),
Optional.empty());
assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
@@ -227,7 +224,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mHeadsUpAppearanceController.onViewDetached();
verify(mHeadsUpManager).removeListener(any());
- verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
+ verify(mDarkIconDispatcher).removeDarkReceiver(any());
verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any());
verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(isNull());
verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4bf67a12963a..dcdf95c4719d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3772,6 +3772,11 @@
The helper is a component that shows the user which keyboard shortcuts they can use. Also
allows the user to add/remove custom shortcuts.[CHAR LIMIT=NONE] -->
<string name="shortcut_customize_mode_remove_shortcut_dialog_title">Remove shortcut?</string>
+ <!-- Title at the top of the keyboard shortcut helper reset shortcut dialog. This dialog allows
+ the user to remove all custom shortcuts the user has set, resetting to default shortcuts only.
+ Shortcut helper is a component that shows the user which keyboard shortcuts they can use. Also
+ allows the user to add/remove custom shortcuts.[CHAR LIMIT=NONE] -->
+ <string name="shortcut_customize_mode_reset_shortcut_dialog_title">Reset back to default?</string>
<!-- Sub title at the top of the keyboard shortcut helper customization dialog. Explains to the
user what action they need to take in the customization dialog to assign a new custom shortcut.
The shortcut customize dialog allows users to add/remove custom shortcuts
@@ -3782,6 +3787,10 @@
users to add/remove custom shortcuts
[CHAR LIMIT=NONE] -->
<string name="shortcut_customize_mode_remove_shortcut_description">This will delete your custom shortcut permanently.</string>
+ <!-- Sub title at the top of the reset custom shortcut dialog. Explains to the user that the action
+ they're about to take will remove all custom shortcuts they have set, resetting to default shortcuts only.
+ The shortcut customize dialog allows users to add/remove custom shortcuts [CHAR LIMIT=NONE] -->
+ <string name="shortcut_customize_mode_reset_shortcut_description">This will delete all your custom shortcuts permanently.</string>
<!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user
hasn't typed in anything in the search box yet. The helper is a component that shows the
user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
@@ -3849,6 +3858,10 @@
confirm and remove previously added custom shortcut. The helper is a component that
shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_customize_dialog_remove_button_label">Remove</string>
+ <!-- Label on the reset shortcut button in keyboard shortcut helper customize dialog, that allows user to
+ confirm and reset all added custom shortcut. The helper is a component that
+ shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_customize_dialog_reset_button_label">Yes, reset</string>
<!-- Label on the cancel button in keyboard shortcut helper customize dialog, that allows user to
cancel and exit shortcut customization dialog, returning to the main shortcut helper page.
The helper is a component that shows the user which keyboard shortcuts they can use.
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index df9f7053c3f3..071cf8a46b9f 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -95,9 +95,10 @@ constructor(
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ // TODO b/362719719 - We should use the configuration controller associated with the display.
private val configurationController: ConfigurationController,
@DisplaySpecific private val resources: Resources,
- private val context: Context,
+ @DisplaySpecific val context: Context,
@Main private val mainExecutor: DelayableExecutor,
@Background private val bgExecutor: Executor,
private val clockBuffers: ClockMessageBuffers,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index add459b84ac1..1083136b570a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -44,6 +44,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -103,7 +104,8 @@ public class KeyguardDisplayManager {
};
@Inject
- public KeyguardDisplayManager(Context context,
+ public KeyguardDisplayManager(
+ @ShadeDisplayAware Context context,
Lazy<NavigationBarController> navigationBarControllerLazy,
DisplayTracker displayTracker,
@Main Executor mainExecutor,
@@ -331,7 +333,8 @@ public class KeyguardDisplayManager {
private boolean mIsInConcurrentDisplayState;
@Inject
- DeviceStateHelper(Context context,
+ DeviceStateHelper(
+ @ShadeDisplayAware Context context,
DeviceStateManager deviceStateManager,
@Main Executor mainExecutor) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 5a02486d5096..07bd813c2420 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -25,6 +25,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
@@ -43,7 +44,7 @@ import javax.inject.Inject
class KeyguardUnfoldTransition
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val keyguardRootView: KeyguardRootView,
private val shadeWindowView: NotificationShadeWindowView,
statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index fd796f657e11..a703b027b691 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -152,6 +152,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
@@ -2163,7 +2164,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@VisibleForTesting
@Inject
protected KeyguardUpdateMonitor(
- Context context,
+ @ShadeDisplayAware Context context,
UserTracker userTracker,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index fc42045c02c8..0305b5e5ab63 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -30,6 +30,7 @@ import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
import com.android.systemui.util.ThreadAssert;
@@ -47,7 +48,7 @@ public abstract class ClockRegistryModule {
@Provides
@SysUISingleton
public static ClockRegistry getClockRegistry(
- @Application Context context,
+ @ShadeDisplayAware Context context,
PluginManager pluginManager,
@Application CoroutineScope scope,
@Main CoroutineDispatcher mainDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 3cf400aa5c16..5b433464c1c6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -18,7 +18,6 @@ package com.android.systemui.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
@@ -48,7 +47,6 @@ import androidx.annotation.NonNull;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
@@ -118,15 +116,13 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
@Override
protected WindowMagnificationController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
- Flags.createWindowlessWindowMagnifier()
- ? TYPE_ACCESSIBILITY_OVERLAY
- : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- /* options */ null);
+ TYPE_ACCESSIBILITY_OVERLAY,
+ /* options */ null);
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
Supplier<SurfaceControlViewHost> scvhSupplier = () ->
- Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext,
- mContext.getDisplay(), new InputTransferToken(), TAG) : null;
+ new SurfaceControlViewHost(mContext,
+ mContext.getDisplay(), new InputTransferToken(), TAG);
return new WindowMagnificationController(
windowContext,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index ffb5f3d47bcc..559e6f767f7b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -20,7 +20,6 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
-import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
@@ -43,7 +42,6 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -77,9 +75,6 @@ class MenuInfoRepository {
private final Context mContext;
private final Configuration mConfiguration;
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityManager.AccessibilityServicesStateChangeListener
- mA11yServicesStateChangeListener = manager -> onTargetFeaturesChanged();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final OnSettingsContentsChanged mSettingsContentsCallback;
private final SecureSettings mSecureSettings;
@@ -147,10 +142,9 @@ class MenuInfoRepository {
}
};
- MenuInfoRepository(Context context, AccessibilityManager accessibilityManager,
+ MenuInfoRepository(Context context,
OnSettingsContentsChanged settingsContentsChanged, SecureSettings secureSettings) {
mContext = context;
- mAccessibilityManager = accessibilityManager;
mConfiguration = new Configuration(context.getResources().getConfiguration());
mSettingsContentsCallback = settingsContentsChanged;
mSecureSettings = secureSettings;
@@ -244,13 +238,6 @@ class MenuInfoRepository {
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mSecureSettings.registerContentObserverForUserSync(
- mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
- /* notifyForDescendants */ false,
- mMenuTargetFeaturesContentObserver,
- UserHandle.USER_CURRENT);
- }
mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants */ false, mMenuSizeContentObserver,
@@ -264,11 +251,6 @@ class MenuInfoRepository {
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
mContext.registerComponentCallbacks(mComponentCallbacks);
-
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mAccessibilityManager.addAccessibilityServicesStateChangeListener(
- mA11yServicesStateChangeListener);
- }
}
void unregisterObserversAndCallbacks() {
@@ -276,11 +258,6 @@ class MenuInfoRepository {
mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);
mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
-
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
- mA11yServicesStateChangeListener);
- }
}
interface OnSettingsContentsChanged {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index cb96e7859fba..cfcaa4fea99b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -42,8 +42,7 @@ class MenuViewLayerController implements IAccessibilityFloatingMenu {
NavigationModeController navigationModeController) {
mWindowManager = viewCaptureAwareWindowManager;
- MenuViewModel menuViewModel = new MenuViewModel(
- context, accessibilityManager, secureSettings);
+ MenuViewModel menuViewModel = new MenuViewModel(context, secureSettings);
MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager);
mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
index f924784a5535..46c407e24fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -17,7 +17,6 @@
package com.android.systemui.accessibility.floatingmenu;
import android.content.Context;
-import android.view.accessibility.AccessibilityManager;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@@ -43,10 +42,9 @@ class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
private final MenuInfoRepository mInfoRepository;
- MenuViewModel(Context context, AccessibilityManager accessibilityManager,
- SecureSettings secureSettings) {
- mInfoRepository = new MenuInfoRepository(context,
- accessibilityManager, /* settingsContentsChanged= */ this, secureSettings);
+ MenuViewModel(Context context, SecureSettings secureSettings) {
+ mInfoRepository = new MenuInfoRepository(context, /* settingsContentsChanged= */ this,
+ secureSettings);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 1fa829a675ec..e5acb8235a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -66,10 +66,22 @@ interface DisplayRepository {
/** Display removal event indicating a display has been removed. */
val displayRemovalEvent: Flow<Int>
- /** Provides the current set of displays. */
+ /**
+ * Provides the current set of displays.
+ *
+ * Consider using [displayIds] if only the [Display.getDisplayId] is needed.
+ */
val displays: StateFlow<Set<Display>>
/**
+ * Provides the current set of display ids.
+ *
+ * Note that it is preferred to use this instead of [displays] if only the
+ * [Display.getDisplayId] is needed.
+ */
+ val displayIds: StateFlow<Set<Int>>
+
+ /**
* Pending display id that can be enabled/disabled.
*
* When `null`, it means there is no pending display waiting to be enabled.
@@ -159,7 +171,7 @@ constructor(
private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
/** Propagate to the listeners only enabled displays */
- private val enabledDisplayIds: Flow<Set<Int>> =
+ private val enabledDisplayIds: StateFlow<Set<Int>> =
allDisplayEvents
.scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
val id = event.displayId
@@ -170,8 +182,8 @@ constructor(
}
}
.distinctUntilChanged()
- .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
.debugLog("enabledDisplayIds")
+ .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
private val defaultDisplay by lazy {
getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
@@ -209,6 +221,8 @@ constructor(
*/
override val displays: StateFlow<Set<Display>> = enabledDisplays
+ override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
+
/**
* Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
* 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
index 12ceedd9cf5a..b990e4cede81 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
@@ -31,15 +31,17 @@ class DozeTransitionListener @Inject constructor() :
override fun transitionTo(oldState: DozeMachine.State, newState: DozeMachine.State) {
this.oldState = oldState
this.newState = newState
- callbacks.forEach { it.onDozeTransition(oldState, newState) }
+
+ val cbs = synchronized(this) { callbacks.toSet() }
+ cbs.forEach { it.onDozeTransition(oldState, newState) }
}
override fun addCallback(callback: DozeTransitionCallback) {
- callbacks.add(callback)
+ synchronized(this) { callbacks.add(callback) }
}
override fun removeCallback(callback: DozeTransitionCallback) {
- callbacks.remove(callback)
+ synchronized(this) { callbacks.remove(callback) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 43b7cedcd767..aee3a457e18a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,7 @@ import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.communal.shared.model.CommunalTransitionKeys;
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.complication.dagger.DreamComplicationComponent;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
@@ -141,8 +142,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
*/
private boolean mBouncerShowing = false;
- private final com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
- mDreamComplicationComponentFactory;
+ private final DreamComplicationComponent.Factory mDreamComplicationComponentFactory;
private final ComplicationComponent.Factory mComplicationComponentFactory;
private final DreamOverlayComponent.Factory mDreamOverlayComponentFactory;
private final AmbientTouchComponent.Factory mAmbientTouchComponentFactory;
@@ -376,8 +376,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Main DelayableExecutor executor,
ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
ComplicationComponent.Factory complicationComponentFactory,
- com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
- dreamComplicationComponentFactory,
+ DreamComplicationComponent.Factory dreamComplicationComponentFactory,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
AmbientTouchComponent.Factory ambientTouchComponentFactory,
DreamOverlayStateController stateController,
@@ -479,9 +478,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mLifecycleOwner,
() -> mExecutor.execute(DreamOverlayService.this::requestExit),
new ViewModelStore(), mTouchInsetManager);
- final com.android.systemui.dreams.complication.dagger.ComplicationComponent
- dreamComplicationComponent = mDreamComplicationComponentFactory.create(
- complicationComponent.getVisibilityController(), mTouchInsetManager);
+ final DreamComplicationComponent dreamComplicationComponent =
+ mDreamComplicationComponentFactory.create(
+ complicationComponent.getVisibilityController(), mTouchInsetManager);
final DreamOverlayComponent dreamOverlayComponent = mDreamOverlayComponentFactory.create(
mLifecycleOwner, complicationComponent.getComplicationHostViewController(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
index f8ae5c28d018..ea5fbc6fa0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
@@ -17,8 +17,8 @@
package com.android.systemui.dreams.complication;
import static com.android.systemui.Flags.removeDreamOverlayHideOnTouch;
-import static com.android.systemui.dreams.complication.dagger.ComplicationModule.COMPLICATIONS_FADE_OUT_DELAY;
-import static com.android.systemui.dreams.complication.dagger.ComplicationModule.COMPLICATIONS_RESTORE_TIMEOUT;
+import static com.android.systemui.dreams.complication.dagger.DreamComplicationModule.COMPLICATIONS_FADE_OUT_DELAY;
+import static com.android.systemui.dreams.complication.dagger.DreamComplicationModule.COMPLICATIONS_RESTORE_TIMEOUT;
import android.util.Log;
import android.view.MotionEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationComponent.kt
index 492c50255b18..17d3acd893e1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationComponent.kt
@@ -6,15 +6,15 @@ import com.android.systemui.touch.TouchInsetManager
import dagger.BindsInstance
import dagger.Subcomponent
-@Subcomponent(modules = [ComplicationModule::class])
-interface ComplicationComponent {
- /** Factory for generating [ComplicationComponent]. */
+@Subcomponent(modules = [DreamComplicationModule::class])
+interface DreamComplicationComponent {
+ /** Factory for generating [DreamComplicationComponent]. */
@Subcomponent.Factory
interface Factory {
fun create(
@BindsInstance visibilityController: Complication.VisibilityController,
- @BindsInstance touchInsetManager: TouchInsetManager
- ): ComplicationComponent
+ @BindsInstance touchInsetManager: TouchInsetManager,
+ ): DreamComplicationComponent
}
fun getHideComplicationTouchHandler(): HideComplicationTouchHandler
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationModule.kt
index 6fd6f4e3d4eb..59af22a6636f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationModule.kt
@@ -1,14 +1,14 @@
package com.android.systemui.dreams.complication.dagger
import android.content.res.Resources
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
import javax.inject.Named
@Module
-object ComplicationModule {
+object DreamComplicationModule {
const val COMPLICATIONS_RESTORE_TIMEOUT = "complication_restore_timeout"
const val COMPLICATIONS_FADE_OUT_DELAY = "complication_fade_out_delay"
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 3171bbc6d6b0..216cb86f8865 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -32,7 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dreams.SystemDialogsCloser;
-import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
+import com.android.systemui.dreams.complication.dagger.DreamComplicationComponent;
import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
import com.android.systemui.dreams.homecontrols.dagger.HomeControlsDataSourceModule;
import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent;
@@ -68,7 +68,7 @@ import javax.inject.Named;
HomeControlsDataSourceModule.class,
},
subcomponents = {
- ComplicationComponent.class,
+ DreamComplicationComponent.class,
DreamOverlayComponent.class,
HomeControlsRemoteServiceComponent.class,
})
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt
index 215ceacaef14..0ed4007d68ef 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt
@@ -78,9 +78,16 @@ fun Expandable.withStateAwareness(
cookie: ActivityTransitionAnimator.TransitionCookie?,
component: ComponentName?,
returnCujType: Int?,
+ isEphemeral: Boolean,
): ActivityTransitionAnimator.Controller? =
delegate
- .activityTransitionController(launchCujType, cookie, component, returnCujType)
+ .activityTransitionController(
+ launchCujType,
+ cookie,
+ component,
+ returnCujType,
+ isEphemeral,
+ )
?.withStateAwareness(onActivityLaunchTransitionStart, onActivityLaunchTransitionEnd)
override fun dialogTransitionController(
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index b82aa817afd8..1504402279b4 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -284,6 +284,7 @@ constructor(
cookie: ActivityTransitionAnimator.TransitionCookie?,
component: ComponentName?,
returnCujType: Int?,
+ isEphemeral: Boolean,
): ActivityTransitionAnimator.Controller? {
val delegatedController =
ActivityTransitionAnimator.Controller.fromView(
@@ -292,6 +293,7 @@ constructor(
cookie,
component,
returnCujType,
+ isEphemeral,
)
return delegatedController?.let { createTransitionControllerDelegate(it) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
index 9ffdafc549c7..36cd40052041 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
@@ -57,7 +57,11 @@ constructor(private val userTracker: UserTracker,
_customInputGesture.onStart { refreshCustomInputGestures() }
private fun refreshCustomInputGestures() {
- _customInputGesture.value = retrieveCustomInputGestures()
+ setCustomInputGestures(inputGestures = retrieveCustomInputGestures())
+ }
+
+ private fun setCustomInputGestures(inputGestures: List<InputGestureData>) {
+ _customInputGesture.value = inputGestures
}
fun retrieveCustomInputGestures(): List<InputGestureData> {
@@ -112,6 +116,22 @@ constructor(private val userTracker: UserTracker,
}
}
+ suspend fun resetAllCustomInputGestures(): ShortcutCustomizationRequestResult {
+ return withContext(bgCoroutineContext) {
+ try {
+ inputManager.removeAllCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ setCustomInputGestures(emptyList())
+ SUCCESS
+ } catch (e: Exception) {
+ Log.w(
+ TAG,
+ "Attempted to remove all custom shortcut but ran into a remote error: $e",
+ )
+ ERROR_OTHER
+ }
+ }
+ }
+
private companion object {
private const val TAG = "CustomInputGesturesRepository"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index d1bd51c23d45..4af378646db3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -169,7 +169,8 @@ constructor(
.firstOrNull { it.action.keyGestureType() == keyGestureType }
}
- suspend fun confirmAndSetShortcutCurrentlyBeingCustomized(): ShortcutCustomizationRequestResult {
+ suspend fun confirmAndSetShortcutCurrentlyBeingCustomized():
+ ShortcutCustomizationRequestResult {
val inputGestureData =
buildInputGestureDataForShortcutBeingCustomized()
?: return ShortcutCustomizationRequestResult.ERROR_OTHER
@@ -184,6 +185,10 @@ constructor(
return customInputGesturesRepository.deleteCustomInputGesture(inputGestureData)
}
+ suspend fun resetAllCustomShortcuts(): ShortcutCustomizationRequestResult {
+ return customInputGesturesRepository.resetAllCustomInputGestures()
+ }
+
private fun Builder.addKeyGestureTypeFromShortcutLabel(): Builder {
val keyGestureType = getKeyGestureTypeFromShortcutBeingCustomizedLabel()
@@ -297,17 +302,13 @@ constructor(
return null
}
- private fun fetchGroupLabelByGestureType(
- @KeyGestureType keyGestureType: Int
- ): String? {
+ private fun fetchGroupLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
inputGestureMaps.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
return context.getString(it)
} ?: return null
}
- private fun fetchShortcutInfoLabelByGestureType(
- @KeyGestureType keyGestureType: Int
- ): String? {
+ private fun fetchShortcutInfoLabelByGestureType(@KeyGestureType keyGestureType: Int): String? {
inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
return context.getString(it)
} ?: return null
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
index 7743c53c6900..ef242678a8ac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt
@@ -46,8 +46,11 @@ constructor(private val customShortcutRepository: CustomShortcutCategoriesReposi
return customShortcutRepository.confirmAndSetShortcutCurrentlyBeingCustomized()
}
- suspend fun deleteShortcutCurrentlyBeingCustomized():
- ShortcutCustomizationRequestResult {
+ suspend fun deleteShortcutCurrentlyBeingCustomized(): ShortcutCustomizationRequestResult {
return customShortcutRepository.deleteShortcutCurrentlyBeingCustomized()
}
+
+ suspend fun resetAllCustomShortcuts(): ShortcutCustomizationRequestResult {
+ return customShortcutRepository.resetAllCustomShortcuts()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
index 2d3e7f6f6448..095de41237cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt
@@ -28,4 +28,6 @@ sealed interface ShortcutCustomizationRequestInfo {
val categoryType: ShortcutCategoryType = ShortcutCategoryType.System,
val subCategoryLabel: String = "",
) : ShortcutCustomizationRequestInfo
+
+ data object Reset : ShortcutCustomizationRequestInfo
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index f28618bb8cf4..bd0430bf96c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutCustomizatio
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutCustomizationViewModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -54,7 +55,8 @@ constructor(
viewModel.shortcutCustomizationUiState.collect { uiState ->
val shouldShowAddDialog = uiState is AddShortcutDialog && !uiState.isDialogShowing
val shouldShowDeleteDialog = uiState is DeleteShortcutDialog && !uiState.isDialogShowing
- if (shouldShowDeleteDialog || shouldShowAddDialog) {
+ val shouldShowResetDialog = uiState is ResetShortcutDialog && !uiState.isDialogShowing
+ if (shouldShowDeleteDialog || shouldShowAddDialog || shouldShowResetDialog) {
dialog = createDialog().also { it.show() }
viewModel.onDialogShown()
} else if (uiState is ShortcutCustomizationUiState.Inactive) {
@@ -83,7 +85,12 @@ constructor(
onKeyPress = { viewModel.onKeyPressed(it) },
onCancel = { dialog.dismiss() },
onConfirmSetShortcut = { coroutineScope.launch { viewModel.onSetShortcut() } },
- onConfirmDeleteShortcut = { coroutineScope.launch { viewModel.deleteShortcutCurrentlyBeingCustomized() } },
+ onConfirmDeleteShortcut = {
+ coroutineScope.launch { viewModel.deleteShortcutCurrentlyBeingCustomized() }
+ },
+ onConfirmResetShortcut = {
+ coroutineScope.launch { viewModel.resetAllCustomShortcuts() }
+ },
)
dialog.setOnDismissListener { viewModel.onDialogDismissed() }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
index 20040c673994..ac6708ae983e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -68,73 +68,133 @@ fun ShortcutCustomizationDialog(
onCancel: () -> Unit,
onConfirmSetShortcut: () -> Unit,
onConfirmDeleteShortcut: () -> Unit,
+ onConfirmResetShortcut: () -> Unit,
) {
when (uiState) {
is ShortcutCustomizationUiState.AddShortcutDialog -> {
- Column(modifier = modifier) {
- Title(uiState.shortcutLabel)
- Description(
- text =
- stringResource(
- id = R.string.shortcut_customize_mode_add_shortcut_description
- )
- )
- PromptShortcutModifier(
- modifier =
- Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp)
- .width(131.dp)
- .height(48.dp),
- defaultModifierKey = uiState.defaultCustomShortcutModifierKey,
- )
- SelectedKeyCombinationContainer(
- shouldShowError = uiState.errorMessage.isNotEmpty(),
- onKeyPress = onKeyPress,
- pressedKeys = uiState.pressedKeys,
- )
- ErrorMessageContainer(uiState.errorMessage)
- DialogButtons(
- onCancel,
- isConfirmButtonEnabled = uiState.pressedKeys.isNotEmpty(),
- onConfirm = onConfirmSetShortcut,
- confirmButtonText =
- stringResource(
- R.string.shortcut_helper_customize_dialog_set_shortcut_button_label
- ),
- )
- }
+ AddShortcutDialog(modifier, uiState, onKeyPress, onCancel, onConfirmSetShortcut)
}
is ShortcutCustomizationUiState.DeleteShortcutDialog -> {
- Column(modifier) {
- Title(
- title =
- stringResource(
- id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title
- )
- )
- Description(
- text =
- stringResource(
- id = R.string.shortcut_customize_mode_remove_shortcut_description
- )
- )
- DialogButtons(
- onCancel = onCancel,
- onConfirm = onConfirmDeleteShortcut,
- confirmButtonText =
- stringResource(
- R.string.shortcut_helper_customize_dialog_remove_button_label
- ),
- )
- }
+ DeleteShortcutDialog(modifier, onCancel, onConfirmDeleteShortcut)
+ }
+ is ShortcutCustomizationUiState.ResetShortcutDialog -> {
+ ResetShortcutDialog(modifier, onCancel, onConfirmResetShortcut)
}
else -> {
- /* No-Op */
+ /* No-op */
}
}
}
@Composable
-fun DialogButtons(
+private fun AddShortcutDialog(
+ modifier: Modifier,
+ uiState: ShortcutCustomizationUiState.AddShortcutDialog,
+ onKeyPress: (KeyEvent) -> Boolean,
+ onCancel: () -> Unit,
+ onConfirmSetShortcut: () -> Unit
+){
+ Column(modifier = modifier) {
+ Title(uiState.shortcutLabel)
+ Description(
+ text =
+ stringResource(
+ id = R.string.shortcut_customize_mode_add_shortcut_description
+ )
+ )
+ PromptShortcutModifier(
+ modifier =
+ Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp)
+ .width(131.dp)
+ .height(48.dp),
+ defaultModifierKey = uiState.defaultCustomShortcutModifierKey,
+ )
+ SelectedKeyCombinationContainer(
+ shouldShowError = uiState.errorMessage.isNotEmpty(),
+ onKeyPress = onKeyPress,
+ pressedKeys = uiState.pressedKeys,
+ )
+ ErrorMessageContainer(uiState.errorMessage)
+ DialogButtons(
+ onCancel,
+ isConfirmButtonEnabled = uiState.pressedKeys.isNotEmpty(),
+ onConfirm = onConfirmSetShortcut,
+ confirmButtonText =
+ stringResource(
+ R.string.shortcut_helper_customize_dialog_set_shortcut_button_label
+ ),
+ )
+ }
+}
+
+@Composable
+private fun DeleteShortcutDialog(
+ modifier: Modifier,
+ onCancel: () -> Unit,
+ onConfirmDeleteShortcut: () -> Unit
+){
+ ConfirmationDialog(
+ modifier = modifier,
+ title =
+ stringResource(
+ id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title
+ ),
+ description =
+ stringResource(
+ id = R.string.shortcut_customize_mode_remove_shortcut_description
+ ),
+ confirmButtonText =
+ stringResource(R.string.shortcut_helper_customize_dialog_remove_button_label),
+ onCancel = onCancel,
+ onConfirm = onConfirmDeleteShortcut,
+ )
+}
+
+@Composable
+private fun ResetShortcutDialog(
+ modifier: Modifier,
+ onCancel: () -> Unit,
+ onConfirmResetShortcut: () -> Unit
+){
+ ConfirmationDialog(
+ modifier = modifier,
+ title =
+ stringResource(
+ id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title
+ ),
+ description =
+ stringResource(
+ id = R.string.shortcut_customize_mode_reset_shortcut_description
+ ),
+ confirmButtonText =
+ stringResource(R.string.shortcut_helper_customize_dialog_reset_button_label),
+ onCancel = onCancel,
+ onConfirm = onConfirmResetShortcut,
+ )
+}
+
+@Composable
+private fun ConfirmationDialog(
+ modifier: Modifier,
+ title: String,
+ description: String,
+ confirmButtonText: String,
+ onConfirm: () -> Unit,
+ onCancel: () -> Unit,
+) {
+ Column(modifier) {
+ Title(title = title)
+ Description(text = description)
+ DialogButtons(
+ onCancel = onCancel,
+ onConfirm = onConfirm,
+ confirmButtonText = confirmButtonText,
+ )
+ }
+}
+
+@Composable
+private fun DialogButtons(
onCancel: () -> Unit,
isConfirmButtonEnabled: Boolean = true,
onConfirm: () -> Unit,
@@ -168,7 +228,7 @@ fun DialogButtons(
}
@Composable
-fun ErrorMessageContainer(errorMessage: String) {
+private fun ErrorMessageContainer(errorMessage: String) {
if (errorMessage.isNotEmpty()) {
Box(modifier = Modifier.padding(horizontal = 16.dp).width(332.dp).height(40.dp)) {
Text(
@@ -185,7 +245,7 @@ fun ErrorMessageContainer(errorMessage: String) {
}
@Composable
-fun SelectedKeyCombinationContainer(
+private fun SelectedKeyCombinationContainer(
shouldShowError: Boolean,
onKeyPress: (KeyEvent) -> Boolean,
pressedKeys: List<ShortcutKey>,
@@ -352,7 +412,7 @@ private fun ActionKeyContainer(defaultModifierKey: ShortcutKey.Icon.ResIdIcon) {
}
@Composable
-fun ActionKeyText() {
+private fun ActionKeyText() {
Text(
text = "Action",
style = MaterialTheme.typography.titleMedium,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 1f37c7de1439..79293077bc4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -471,6 +471,9 @@ private fun EndSidePanel(
is ShortcutCustomizationRequestInfo.Delete ->
onCustomizationRequested(requestInfo.copy(categoryType = category.type))
+
+ ShortcutCustomizationRequestInfo.Reset ->
+ onCustomizationRequested(requestInfo)
}
},
)
@@ -535,6 +538,9 @@ private fun SubCategoryContainerDualPane(
onCustomizationRequested(
requestInfo.copy(subCategoryLabel = subCategory.label)
)
+
+ ShortcutCustomizationRequestInfo.Reset ->
+ onCustomizationRequested(requestInfo)
}
},
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
index 990257d642ff..bfc9486552a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt
@@ -23,11 +23,17 @@ sealed interface ShortcutCustomizationUiState {
val shortcutLabel: String,
val errorMessage: String = "",
val defaultCustomShortcutModifierKey: ShortcutKey.Icon.ResIdIcon,
- val isDialogShowing: Boolean,
+ val isDialogShowing: Boolean = false,
val pressedKeys: List<ShortcutKey> = emptyList(),
) : ShortcutCustomizationUiState
- data class DeleteShortcutDialog(val isDialogShowing: Boolean) : ShortcutCustomizationUiState
+ data class DeleteShortcutDialog(
+ val isDialogShowing: Boolean = false
+ ) : ShortcutCustomizationUiState
+
+ data class ResetShortcutDialog(
+ val isDialogShowing: Boolean = false
+ ) : ShortcutCustomizationUiState
data object Inactive : ShortcutCustomizationUiState
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index b467bb481443..76a2e6095f01 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomization
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog
+import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.ResetShortcutDialog
import com.android.systemui.res.R
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -82,6 +83,10 @@ constructor(
_shortcutCustomizationUiState.value = DeleteShortcutDialog(isDialogShowing = false)
shortcutCustomizationInteractor.onCustomizationRequested(requestInfo)
}
+
+ ShortcutCustomizationRequestInfo.Reset -> {
+ _shortcutCustomizationUiState.value = ResetShortcutDialog(isDialogShowing = false)
+ }
}
}
@@ -89,6 +94,7 @@ constructor(
_shortcutCustomizationUiState.update { uiState ->
(uiState as? AddShortcutDialog)?.copy(isDialogShowing = true)
?: (uiState as? DeleteShortcutDialog)?.copy(isDialogShowing = true)
+ ?: (uiState as? ResetShortcutDialog)?.copy(isDialogShowing = true)
?: uiState
}
}
@@ -134,8 +140,18 @@ constructor(
}
suspend fun deleteShortcutCurrentlyBeingCustomized() {
- val result =
- shortcutCustomizationInteractor.deleteShortcutCurrentlyBeingCustomized()
+ val result = shortcutCustomizationInteractor.deleteShortcutCurrentlyBeingCustomized()
+
+ _shortcutCustomizationUiState.update { uiState ->
+ when (result) {
+ ShortcutCustomizationRequestResult.SUCCESS -> ShortcutCustomizationUiState.Inactive
+ else -> uiState
+ }
+ }
+ }
+
+ suspend fun resetAllCustomShortcuts() {
+ val result = shortcutCustomizationInteractor.resetAllCustomShortcuts()
_shortcutCustomizationUiState.update { uiState ->
when (result) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 69856151e41c..3d6cf2f9d1c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -187,10 +187,6 @@ object DeviceEntryIconViewBinder {
launch("$TAG#fpIconView.viewModel") {
fgViewModel.viewModel.collect { viewModel ->
Log.d(TAG, "Updating device entry icon image state $viewModel")
- fgIconView.setImageState(
- view.getIconState(viewModel.type, viewModel.useAodVariant),
- /* merge */ false,
- )
if (viewModel.type.contentDescriptionResId != -1) {
fgIconView.contentDescription =
fgIconView.resources.getString(
@@ -205,6 +201,14 @@ object DeviceEntryIconViewBinder {
viewModel.padding,
viewModel.padding,
)
+ // Set image state at the end after updating other view state. This
+ // method forces the ImageView to recompute the bounds of the drawable.
+ fgIconView.setImageState(
+ view.getIconState(viewModel.type, viewModel.useAodVariant),
+ /* merge */ false,
+ )
+ // Invalidate, just in case the padding changes just after icon changes
+ fgIconView.invalidate()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 0a4e8c660761..b1719107fae1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -68,6 +68,7 @@ import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.window.BackEvent;
import androidx.annotation.DimenRes;
@@ -585,6 +586,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mNonLinearFactor = getDimenFloat(res,
com.android.internal.R.dimen.back_progress_non_linear_factor);
updateBackAnimationThresholds();
+ mBackgroundExecutor.execute(this::disableNavBarVirtualKeyHapticFeedback);
}
private float getDimenFloat(Resources res, @DimenRes int resId) {
@@ -1287,6 +1289,15 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
}
+ private void disableNavBarVirtualKeyHapticFeedback() {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .setNavBarVirtualKeyHapticFeedbackEnabled(false);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to disable navigation bar button haptics: ", e);
+ }
+ }
+
public void dump(PrintWriter pw) {
pw.println("EdgeBackGestureHandler:");
pw.println(" mIsEnabled=" + mIsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 7ccdf0ac301a..946eccd05177 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -25,6 +25,7 @@ import android.view.View;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -65,7 +66,7 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
QSContainerImpl view,
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
- ConfigurationController configurationController,
+ @ShadeDisplayAware ConfigurationController configurationController,
FalsingManager falsingManager) {
super(view);
mQsPanelController = qsPanelController;
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 a222b3cbd08c..c606ce422d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -43,6 +43,7 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -107,7 +108,8 @@ public class QSCustomizerController extends ViewController<QSCustomizer> {
protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,
QSHost qsHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
KeyguardStateController keyguardStateController, LightBarController lightBarController,
- ConfigurationController configurationController, UiEventLogger uiEventLogger) {
+ @ShadeDisplayAware ConfigurationController configurationController,
+ UiEventLogger uiEventLogger) {
super(view);
mTileQueryHelper = tileQueryHelper;
mQsHost = qsHost;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index bec6581e54c9..0cee7ddfeaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -42,6 +42,7 @@ import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -78,7 +79,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- ConfigurationController configurationController,
+ @ShadeDisplayAware ConfigurationController configurationController,
BatteryController batteryController,
LocationController locationController
) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 4d77e3ecea7b..41d56c51fe77 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -27,9 +27,10 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.view.Display;
import android.view.IWindow;
@@ -73,6 +74,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.settings.SecureSettings;
import dagger.Lazy;
@@ -130,6 +132,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private final SysuiColorExtractor mColorExtractor;
private final NotificationShadeWindowModel mNotificationShadeWindowModel;
+ private final SecureSettings mSecureSettings;
/**
* Layout params would be aggregated and dispatched all at once if this is > 0.
*
@@ -163,6 +166,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
Lazy<SelectedUserInteractor> userInteractor,
UserTracker userTracker,
NotificationShadeWindowModel notificationShadeWindowModel,
+ SecureSettings secureSettings,
Lazy<CommunalInteractor> communalInteractor) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
@@ -178,6 +182,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
mNotificationShadeWindowModel = notificationShadeWindowModel;
+ mSecureSettings = secureSettings;
// prefix with {slow} to make sure this dumps at the END of the critical section.
dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
mAuthController = authController;
@@ -400,7 +405,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
(long) mLpChanged.preferredMaxDisplayRefreshRate);
}
- if (state.bouncerShowing && !isDebuggable()) {
+ if (state.bouncerShowing && !isSecureWindowsDisabled()) {
mLpChanged.flags |= LayoutParams.FLAG_SECURE;
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_SECURE;
@@ -413,8 +418,11 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
}
- protected boolean isDebuggable() {
- return Build.IS_DEBUGGABLE;
+ private boolean isSecureWindowsDisabled() {
+ return mSecureSettings.getIntForUser(
+ Settings.Secure.DISABLE_SECURE_WINDOWS,
+ 0,
+ UserHandle.USER_CURRENT) == 1;
}
private void adjustScreenOrientation(NotificationShadeWindowState state) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index 1b22ee40009b..bb96b0b3ce50 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -31,9 +31,12 @@ interface ShadeDisplayPolicy {
@Module
interface ShadeDisplayPolicyModule {
+
+ @Binds fun provideDefaultPolicy(impl: StatusBarTouchShadeDisplayPolicy): ShadeDisplayPolicy
+
@IntoSet
@Binds
- fun provideDefaultPolicyToSet(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
+ fun provideDefaultDisplayPolicyToSet(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
@IntoSet
@Binds
@@ -41,5 +44,9 @@ interface ShadeDisplayPolicyModule {
impl: AnyExternalShadeDisplayPolicy
): ShadeDisplayPolicy
- @Binds fun provideDefaultPolicy(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
+ @Binds
+ @IntoSet
+ fun provideStatusBarTouchShadeDisplayPolicy(
+ impl: StatusBarTouchShadeDisplayPolicy
+ ): ShadeDisplayPolicy
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
index 13e766409bab..d43aad70368e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
@@ -22,12 +22,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
/** Policy to specify a display id explicitly. */
-open class SpecificDisplayIdPolicy(displayId: Int) : ShadeDisplayPolicy {
- override val name: String
- get() = "display_${displayId}_policy"
+open class SpecificDisplayIdPolicy(id: Int) : ShadeDisplayPolicy {
+ override val name: String = "display_${id}_policy"
- override val displayId: StateFlow<Int> = MutableStateFlow(displayId)
+ override val displayId: StateFlow<Int> = MutableStateFlow(id)
}
-class DefaultShadeDisplayPolicy @Inject constructor() :
+class DefaultDisplayShadePolicy @Inject constructor() :
SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
new file mode 100644
index 000000000000..22e9487af84c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.display
+
+import android.util.Log
+import android.view.Display
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/**
+ * Moves the shade on the last display that received a status bar touch.
+ *
+ * If the display is removed, falls back to the default one.
+ */
+@SysUISingleton
+class StatusBarTouchShadeDisplayPolicy
+@Inject
+constructor(displayRepository: DisplayRepository, @Background val backgroundScope: CoroutineScope) :
+ ShadeDisplayPolicy {
+ override val name: String
+ get() = "status_bar_latest_touch"
+
+ private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+ private val availableDisplayIds: StateFlow<Set<Int>> = displayRepository.displayIds
+
+ override val displayId: StateFlow<Int>
+ get() = currentDisplayId
+
+ private var removalListener: Job? = null
+
+ /** Called when the status bar on the given display is touched. */
+ fun onStatusBarTouched(statusBarDisplayId: Int) {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ if (statusBarDisplayId !in availableDisplayIds.value) {
+ Log.e(TAG, "Got touch on unknown display $statusBarDisplayId")
+ return
+ }
+ currentDisplayId.value = statusBarDisplayId
+ if (removalListener == null) {
+ // Lazy start this at the first invocation. it's fine to let it run also when the policy
+ // is not selected anymore, as the job doesn't do anything until someone subscribes to
+ // displayId.
+ removalListener = monitorDisplayRemovals()
+ }
+ }
+
+ private fun monitorDisplayRemovals(): Job {
+ return backgroundScope.launchTraced("StatusBarTouchDisplayPolicy#monitorDisplayRemovals") {
+ currentDisplayId.subscriptionCount
+ .map { it > 0 }
+ .distinctUntilChanged()
+ // When Active is false, no collect happens, and the old one is cancelled.
+ // This is needed to prevent "availableDisplayIds" collection while nobody is
+ // listening at the flow provided by this class.
+ .collectLatest { active ->
+ if (active) {
+ availableDisplayIds.collect { availableIds ->
+ if (currentDisplayId.value !in availableIds) {
+ currentDisplayId.value = Display.DEFAULT_DISPLAY
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "StatusBarTouchDisplayPolicy"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 60d846ebacac..2d6ed70eb905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -55,6 +55,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
@@ -84,7 +85,7 @@ public class InstantAppNotifier
@Inject
public InstantAppNotifier(
- Context context,
+ @ShadeDisplayAware Context context,
CommandQueue commandQueue,
UserTracker userTracker,
@Main Executor mainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 11db2fc77170..d5551b16842e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -50,6 +50,7 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
@@ -69,7 +70,8 @@ public class ExpandableNotificationRowDragController {
private NotificationPanelLogger mNotificationPanelLogger;
@Inject
- public ExpandableNotificationRowDragController(Context context,
+ public ExpandableNotificationRowDragController(
+ @ShadeDisplayAware Context context,
HeadsUpManager headsUpManager,
ShadeController shadeController,
NotificationPanelLogger notificationPanelLogger) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 01efd5de93ed..f8f29ff59154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -117,7 +117,7 @@ constructor(
private val interactor: SharedNotificationContainerInteractor,
dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 677ed9f1ccdc..6cad68ffe8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -27,7 +27,6 @@ import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.dagger.qualifiers.DisplaySpecific;
-import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
@@ -98,7 +98,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
@VisibleForTesting
float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
- private boolean mShown;
+ private PinnedStatus mPinnedStatus = PinnedStatus.NotPinned;
private final ViewClippingUtil.ClippingParameters mParentClippingParams =
new ViewClippingUtil.ClippingParameters() {
@Override
@@ -108,7 +108,6 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
};
private boolean mAnimationsEnabled = true;
private final KeyguardStateController mKeyguardStateController;
- private final FeatureFlagsClassic mFeatureFlags;
private final HeadsUpNotificationIconInteractor mHeadsUpNotificationIconInteractor;
@VisibleForTesting
@@ -127,7 +126,6 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
NotificationRoundnessManager notificationRoundnessManager,
HeadsUpStatusBarView headsUpStatusBarView,
Clock clockView,
- FeatureFlagsClassic featureFlags,
HeadsUpNotificationIconInteractor headsUpNotificationIconInteractor,
@Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) {
super(headsUpStatusBarView);
@@ -145,7 +143,6 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
mStackScrollerController = stackScrollerController;
mShadeViewController = shadeViewController;
- mFeatureFlags = featureFlags;
mHeadsUpNotificationIconInteractor = headsUpNotificationIconInteractor;
mStackScrollerController.setHeadsUpAppearanceController(this);
mClockView = clockView;
@@ -157,7 +154,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (shouldBeVisible()) {
- updateTopEntry("onLayoutChange");
+ updateTopEntry();
// trigger scroller to notify the latest panel translation
mStackScrollerController.requestLayout();
@@ -207,7 +204,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- updateTopEntry("onHeadsUpPinned");
+ updateTopEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
@@ -218,7 +215,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
mPhoneStatusBarTransitions.onHeadsUpStateChanged(isHeadsUp);
}
- private void updateTopEntry(String reason) {
+ private void updateTopEntry() {
NotificationEntry newEntry = null;
if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
@@ -226,27 +223,29 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
NotificationEntry previousEntry = mView.getShowingEntry();
mView.setEntry(newEntry);
if (newEntry != previousEntry) {
- boolean animateIsolation = false;
if (newEntry == null) {
// no heads up anymore, lets start the disappear animation
-
- setShown(false);
- animateIsolation = !isExpanded();
+ setPinnedStatus(PinnedStatus.NotPinned);
} else if (previousEntry == null) {
// We now have a headsUp and didn't have one before. Let's start the disappear
// animation
- setShown(true);
- animateIsolation = !isExpanded();
+ setPinnedStatus(PinnedStatus.PinnedBySystem);
+ }
+
+ String isolatedIconKey;
+ if (newEntry != null) {
+ isolatedIconKey = newEntry.getRepresentativeEntry().getKey();
+ } else {
+ isolatedIconKey = null;
}
- mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
- newEntry == null ? null : newEntry.getRepresentativeEntry().getKey());
+ mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(isolatedIconKey);
}
}
- private void setShown(boolean isShown) {
- if (mShown != isShown) {
- mShown = isShown;
- if (isShown) {
+ private void setPinnedStatus(PinnedStatus pinnedStatus) {
+ if (mPinnedStatus != pinnedStatus) {
+ mPinnedStatus = pinnedStatus;
+ if (pinnedStatus.isPinned()) {
updateParentClipping(false /* shouldClip */);
mView.setVisibility(View.VISIBLE);
show(mView);
@@ -330,8 +329,8 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
}
@VisibleForTesting
- public boolean isShown() {
- return mShown;
+ public PinnedStatus getPinnedStatus() {
+ return mPinnedStatus;
}
/**
@@ -355,7 +354,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
- updateTopEntry("onHeadsUpUnPinned");
+ updateTopEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
@@ -373,7 +372,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
updateHeadsUpHeaders();
}
if (isExpanded() != oldIsExpanded) {
- updateTopEntry("setAppearFraction");
+ updateTopEntry();
}
}
@@ -447,11 +446,11 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
}
public void onStateChanged() {
- updateTopEntry("onStateChanged");
+ updateTopEntry();
}
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
- updateTopEntry("onFullyHiddenChanged");
+ updateTopEntry();
}
}
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 16e023ce17fd..f19d707046f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -39,7 +39,9 @@ import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.policy.Clock
@@ -52,6 +54,7 @@ import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
import com.android.systemui.util.view.ViewUtil
+import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
@@ -79,6 +82,7 @@ private constructor(
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
+ private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
) : ViewController<PhoneStatusBarView>(view) {
private lateinit var battery: BatteryMeterView
@@ -226,6 +230,9 @@ private constructor(
!upOrCancel || shadeController.isExpandedVisible,
)
}
+ if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) {
+ lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(context.displayId)
+ }
}
private fun addDarkReceivers() {
@@ -344,6 +351,7 @@ private constructor(
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
@DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
+ private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
@@ -371,6 +379,7 @@ private constructor(
statusOverlayHoverListenerFactory,
darkIconDispatcher,
statusBarContentInsetsProviderStore.defaultDisplay,
+ lazyStatusBarShadeDisplayPolicy,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index d210e93e36f1..c03ba0178fd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -26,7 +26,12 @@ import java.io.PrintWriter;
/**
* Source of truth for keyguard state: If locked, occluded, has password, trusted etc.
+ *
+ * @deprecated this class is not supported when KEYGUARD_WM_STATE_REFACTOR is enabled.
+ * Use {@link com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor}
+ * or {@link com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor} instead.
*/
+@Deprecated
public interface KeyguardStateController extends CallbackController<Callback>, Dumpable {
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index dddaabb66022..50d0049dbcd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,12 +26,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.PointF;
-
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FlingAnimation;
@@ -51,7 +49,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -72,17 +69,13 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
secureSettings));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 400b3b388c31..f4580c173579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -159,8 +159,7 @@ public class MenuViewLayerTest extends SysuiTestCase {
new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
- mMenuViewModel = new MenuViewModel(
- mSpyContext, mStubAccessibilityManager, mSecureSettings);
+ mMenuViewModel = new MenuViewModel(mSpyContext, mSecureSettings);
MenuViewAppearance menuViewAppearance = new MenuViewAppearance(
mSpyContext, mStubWindowManager);
mMenuView = spy(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index b3cccea97e08..492d5f3657da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -277,6 +277,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
() -> mSelectedUserInteractor,
mUserTracker,
mKosmos.getNotificationShadeWindowModel(),
+ mSecureSettings,
mKosmos::getCommunalInteractor);
mFeatureFlags = new FakeFeatureFlags();
mSetFlagsRule.disableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 69efa87a9cac..abdd79761ce0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -33,6 +33,7 @@ import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.flags.FeatureFlags
@@ -44,6 +45,7 @@ import com.android.systemui.shade.ShadeControllerImpl
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
@@ -60,12 +62,14 @@ import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
import java.util.Optional
import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -98,6 +102,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Mock private lateinit var shadeLogger: ShadeLogger
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var mStatusBarLongPressGestureDetector: StatusBarLongPressGestureDetector
+ @Mock private lateinit var statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy
private lateinit var statusBarWindowStateController: StatusBarWindowStateController
private lateinit var view: PhoneStatusBarView
@@ -329,6 +334,30 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onTouch_actionDown_propagatesToDisplayPolicy() {
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(mContext.displayId))
+ }
+
+ @Test
+ @EnableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onTouch_actionUp_notPropagatesToDisplayPolicy() {
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0))
+
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ }
+
+ @Test
+ @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onTouch_shadeWindowGoesAroundDisabled_notPropagatesToDisplayPolicy() {
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ }
+
+ @Test
fun shadeIsExpandedOnStatusIconMouseClick() {
val view = createViewMock()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -402,6 +431,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
mStatusOverlayHoverListenerFactory,
fakeDarkIconDispatcher,
statusBarContentInsetsProviderStore,
+ Lazy { statusBarTouchShadeDisplayPolicy },
)
.create(view)
.also { it.init() }
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 a1c9022e18bd..eb91b1e4a89e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -162,6 +162,7 @@ import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvision
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.Flags;
@@ -442,6 +443,7 @@ public class BubblesTest extends SysuiTestCase {
() -> mSelectedUserInteractor,
mUserTracker,
mNotificationShadeWindowModel,
+ new FakeSettings(),
mKosmos::getCommunalInteractor
);
mNotificationShadeWindowController.fetchWindowRootView();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index ddcc92691f5b..3fc60e339543 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -48,6 +48,7 @@ fun createPendingDisplay(id: Int = 0): DisplayRepository.PendingDisplay =
/** Fake [DisplayRepository] implementation for testing. */
class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val flow = MutableStateFlow<Set<Display>>(emptySet())
+ private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
private val pendingDisplayFlow =
MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
@@ -63,11 +64,13 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
suspend fun addDisplay(display: Display) {
flow.value += display
+ displayIdFlow.value += display.displayId
displayAdditionEventFlow.emit(display)
}
suspend fun removeDisplay(displayId: Int) {
flow.value = flow.value.filter { it.displayId != displayId }.toSet()
+ displayIdFlow.value = displayIdFlow.value.filter { it != displayId }.toSet()
displayRemovalEventFlow.emit(displayId)
}
@@ -83,10 +86,13 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
override val displays: StateFlow<Set<Display>>
get() = flow
+ override val displayIds: StateFlow<Set<Int>>
+ get() = displayIdFlow
+
override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
get() = pendingDisplayFlow
- val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val defaultDisplayOff: Flow<Boolean>
get() = _defaultDisplayOff.asStateFlow()
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index de3c5f2c23ab..9644a52a749e 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -63,8 +63,6 @@ import java.util.function.BiConsumer;
* - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
*/
public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
- public static final String TAG = "Ravenwood";
-
/** Scope of a hook. */
public enum Scope {
Class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 239c8061b757..9eff20ad70e6 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -45,7 +45,7 @@ import java.util.concurrent.Executor;
import java.util.function.Supplier;
public class RavenwoodContext extends RavenwoodBaseContext {
- private static final String TAG = "Ravenwood";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private final Object mLock = new Object();
private final String mPackageName;
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
index 77275c445dd9..3cb2c67adf09 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
@@ -27,8 +27,6 @@ import org.junit.runner.Description;
* Calculates which tests need to be executed on Ravenwood.
*/
public class RavenwoodEnablementChecker {
- private static final String TAG = "RavenwoodDisablementChecker";
-
private RavenwoodEnablementChecker() {
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index 6dfcf4ce03cf..a5d0bfd51a0f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -40,7 +40,7 @@ import java.util.Set;
* All members must be called from the runner's main thread.
*/
public final class RavenwoodRunnerState {
- private static final String TAG = "RavenwoodRunnerState";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private static final String RAVENWOOD_RULE_ERROR =
"RavenwoodRule(s) are not executed in the correct order";
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 172cec3b8e13..930914f586eb 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -96,7 +96,7 @@ import java.util.function.Supplier;
* Responsible for initializing and the environment.
*/
public class RavenwoodRuntimeEnvironmentController {
- private static final String TAG = "RavenwoodRuntimeEnvironmentController";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private RavenwoodRuntimeEnvironmentController() {
}
@@ -105,6 +105,9 @@ public class RavenwoodRuntimeEnvironmentController {
private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+ private static final String ANDROID_LOG_TAGS = "ANDROID_LOG_TAGS";
+ private static final String RAVENWOOD_ANDROID_LOG_TAGS = "RAVENWOOD_" + ANDROID_LOG_TAGS;
+
/**
* When enabled, attempt to dump all thread stacks just before we hit the
* overall Tradefed timeout, to aid in debugging deadlocks.
@@ -232,20 +235,22 @@ public class RavenwoodRuntimeEnvironmentController {
// Make sure libravenwood_runtime is loaded.
System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
+ Log_ravenwood.setLogLevels(getLogTags());
Log_ravenwood.onRavenwoodRuntimeNativeReady();
// Do the basic set up for the android sysprops.
RavenwoodSystemProperties.initialize();
+ // Enable all log levels for native logging, until we'll have a way to change the native
+ // side log level at runtime.
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
// before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
- if (RAVENWOOD_VERBOSE_LOGGING) {
- RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
- try {
- Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
+ // This would also prevent libbase from crashing the process (b/381112373) because
+ // the string format it accepts is very limited.
+ try {
+ Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
}
// Make sure libandroid_runtime is loaded.
@@ -333,6 +338,18 @@ public class RavenwoodRuntimeEnvironmentController {
initializeCompatIds();
}
+ /**
+ * Get log tags from environmental variable.
+ */
+ @Nullable
+ private static String getLogTags() {
+ var logTags = System.getenv(RAVENWOOD_ANDROID_LOG_TAGS);
+ if (logTags == null) {
+ logTags = System.getenv(ANDROID_LOG_TAGS);
+ }
+ return logTags;
+ }
+
private static void loadRavenwoodProperties() {
var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 99b38ed4c92c..fac07910be11 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -34,7 +34,7 @@ import java.util.Set;
* A class to manage the core default system properties of the Ravenwood environment.
*/
public class RavenwoodSystemProperties {
- private static final String TAG = "RavenwoodSystemProperties";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
/** We pull in properties from this file. */
private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index 787058545fed..c8b10d20843f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -41,7 +41,7 @@ import java.util.Map;
* `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_latest.csv`.
*/
public class RavenwoodTestStats {
- private static final String TAG = "RavenwoodTestStats";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private static final String HEADER = "Module,Class,OuterClass,Passed,Failed,Skipped";
private static RavenwoodTestStats sInstance;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
index 31a14164bd51..f3688d664142 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
@@ -35,7 +35,7 @@ import org.junit.runners.model.RunnerBuilder;
import org.junit.runners.model.TestClass;
abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable, Orderable {
- private static final String TAG = "Ravenwood";
+ public static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
boolean mRealRunnerTakesRunnerBuilder = false;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index e49d3d934e9f..d8cde0e029c9 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -41,7 +41,7 @@ import java.util.regex.Pattern;
*/
@Deprecated
public final class RavenwoodRule implements TestRule {
- private static final String TAG = "RavenwoodRule";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
@@ -193,6 +193,12 @@ public final class RavenwoodRule implements TestRule {
return IS_ON_RAVENWOOD;
}
+ private static void ensureOnRavenwood(String featureName) {
+ if (!IS_ON_RAVENWOOD) {
+ throw new RuntimeException(featureName + " is only supported on Ravenwood.");
+ }
+ }
+
/**
* @deprecated Use
* {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
@@ -242,6 +248,40 @@ public final class RavenwoodRule implements TestRule {
return System.currentTimeMillis();
}
+ /**
+ * Equivalent to setting the ANDROID_LOG_TAGS environmental variable.
+ *
+ * See https://developer.android.com/tools/logcat#filteringOutput for the string format.
+ *
+ * NOTE: this works only on Ravenwood.
+ */
+ public static void setAndroidLogTags(@Nullable String androidLogTags) {
+ ensureOnRavenwood("RavenwoodRule.setAndroidLogTags()");
+ try {
+ Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood");
+ var setter = logRavenwoodClazz.getMethod("setLogLevels", String.class);
+ setter.invoke(null, androidLogTags);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Set a log level for a given tag. Pass NULL to {@code tag} to change the default level.
+ *
+ * NOTE: this works only on Ravenwood.
+ */
+ public static void setLogLevel(@Nullable String tag, int level) {
+ ensureOnRavenwood("RavenwoodRule.setLogLevel()");
+ try {
+ Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood");
+ var setter = logRavenwoodClazz.getMethod("setLogLevel", String.class, int.class);
+ setter.invoke(null, tag, level);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
// Below are internal to ravenwood. Don't use them from normal tests...
public static class RavenwoodPrivate {
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index b4b751788c6e..8d9bcd5b242f 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -31,8 +31,6 @@ import org.junit.runners.model.TestClass;
* This is only used when a real device-side test has Ravenizer enabled.
*/
public class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
- private static final String TAG = "Ravenwood";
-
private static class NopRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 2a04d4469ef4..a967a3fff0d7 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -33,7 +33,7 @@ import java.util.Arrays;
import java.util.function.Supplier;
public class RavenwoodCommonUtils {
- private static final String TAG = "RavenwoodCommonUtils";
+ public static final String TAG = "Ravenwood";
private RavenwoodCommonUtils() {
}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
index 7b26fe531e7e..7ab9cda378b7 100644
--- a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
+++ b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
@@ -15,8 +15,10 @@
*/
package android.util;
+import android.annotation.Nullable;
import android.util.Log.Level;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.RuntimeInit;
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.common.RavenwoodCommonUtils;
@@ -24,7 +26,9 @@ import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
/**
* Ravenwood "native substitution" class for {@link android.util.Log}.
@@ -35,16 +39,100 @@ import java.util.Locale;
*/
public class Log_ravenwood {
- public static final SimpleDateFormat sTimestampFormat =
+ private static final SimpleDateFormat sTimestampFormat =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
- public static boolean isLoggable(String tag, @Level int level) {
- return true;
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static int sDefaultLogLevel;
+
+ @GuardedBy("sLock")
+ private static final Map<String, Integer> sTagLogLevels = new HashMap<>();
+
+ /**
+ * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setAndroidLogTags(String)}
+ * via reflections.
+ */
+ public static void setLogLevels(String androidLogTags) {
+ var map = parseLogLevels(androidLogTags);
+
+ synchronized (sLock) {
+ sTagLogLevels.clear();
+ sTagLogLevels.putAll(map);
+
+ var def = map.get("*");
+ sDefaultLogLevel = def != null ? def : Log.VERBOSE;
+ }
+ }
+
+ private static Map<String, Integer> parseLogLevels(String androidLogTags) {
+ final Map<String, Integer> ret = new HashMap<>();
+
+ if (androidLogTags == null) {
+ return ret;
+ }
+
+ String[] tagPairs = androidLogTags.trim().split("\\s+");
+ for (String tagPair : tagPairs) {
+ String[] parts = tagPair.split(":");
+ if (parts.length == 2) {
+ String tag = parts[0];
+ try {
+ int priority = switch (parts[1].toUpperCase(Locale.ROOT)) {
+ case "V": yield Log.VERBOSE;
+ case "D": yield Log.DEBUG;
+ case "I": yield Log.INFO;
+ case "W": yield Log.WARN;
+ case "E": yield Log.ERROR;
+ case "F": yield Log.ERROR + 1; // Not used in the java side.
+ case "S": yield Integer.MAX_VALUE; // Silent
+ default: throw new IllegalArgumentException(
+ "Invalid priority level for tag: " + tag);
+ };
+
+ ret.put(tag, priority);
+ } catch (IllegalArgumentException e) {
+ System.err.println(e.getMessage());
+ }
+ } else {
+ System.err.println("Invalid tag format: " + tagPair);
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setLogLevel(String, int)}
+ * via reflections. Pass NULL to {@code tag} to set the default level.
+ */
+ public static void setLogLevel(@Nullable String tag, int level) {
+ synchronized (sLock) {
+ if (tag == null) {
+ sDefaultLogLevel = level;
+ } else {
+ sTagLogLevels.put(tag, level);
+ }
+ }
+ }
+
+ /**
+ * Replaces {@link Log#isLoggable}.
+ */
+ public static boolean isLoggable(String tag, @Level int priority) {
+ synchronized (sLock) {
+ var threshold = sTagLogLevels.get(tag);
+ if (threshold == null) {
+ threshold = sDefaultLogLevel;
+ }
+ return priority >= threshold;
+ }
}
public static int println_native(int bufID, int priority, String tag, String msg) {
- if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) {
- return msg.length(); // No verbose logging.
+ if (!isLoggable(tag, priority)) {
+ return msg.length();
}
final String prio;
diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py
new file mode 100755
index 000000000000..bdc1de0c44f4
--- /dev/null
+++ b/ravenwood/scripts/extract-last-soong-commands.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# This script extracts all the commands executed in the last soong run,
+# and write them into a script file, and print the filename.
+#
+# All the commands are commented out. Uncomment what you want to execute as
+# needed before running it.
+
+import datetime
+import gzip
+import os
+import re
+import shlex
+import sys
+
+re_command = re.compile(r''' ^\[.*?\] \s* (.*) ''', re.X)
+
+HEADER = r'''#!/bin/bash
+
+set -e # Stop on a failed command
+
+cd "${ANDROID_BUILD_TOP:?}"
+
+'''
+
+OUT_SCRIPT_DIR = "/tmp/"
+OUT_SCRIPT_FORMAT = "soong-rerun-%Y-%m-%d_%H-%M-%S.sh"
+
+def main(args):
+ log = os.environ["ANDROID_BUILD_TOP"] + "/out/verbose.log.gz"
+ outdir = "/tmp/"
+ outfile = outdir + datetime.datetime.now().strftime(OUT_SCRIPT_FORMAT)
+
+ with open(outfile, "w") as out:
+ out.write(HEADER)
+
+ with gzip.open(log) as f:
+ for line in f:
+ s = line.decode("utf-8")
+
+ if s.startswith("verbose"):
+ continue
+ if re.match('^\[.*bootstrap blueprint', s):
+ continue
+
+ s = s.rstrip()
+
+ m = re_command.search(s)
+ if m:
+ command = m.groups()[0]
+
+ out.write('#========\n')
+
+ # Show the full command line before executing it.
+ out.write('#echo ' + shlex.quote(command) + '\n')
+ out.write('\n')
+
+ # Execute the command.
+ out.write('#' + command + '\n')
+
+ out.write('\n')
+
+ continue
+
+ if s.startswith("FAILED:"):
+ break
+
+ os.chmod(outfile, 0o755)
+ print(outfile)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java
new file mode 100644
index 000000000000..74c1f3f686aa
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.coretest;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import org.junit.Test;
+
+public class RavenwoodLogLevelTest {
+ /**
+ * Assert that the `priority` is loggable, but one level below is not.
+ */
+ private void assertBarelyLoggable(String tag, int priority) {
+ assertEquals(true, Log.isLoggable(tag, priority));
+ assertEquals(false, Log.isLoggable(tag, priority - 1));
+ }
+
+ @Test
+ public void testDefaultLogTags() {
+ RavenwoodRule.setAndroidLogTags(null);
+
+ // Info should always be loggable.
+ assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+ assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+ assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+ }
+
+ @Test
+ public void testAllVerbose() {
+ RavenwoodRule.setAndroidLogTags("*:V");
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+ assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+ assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+ }
+
+ @Test
+ public void testAllSilent() {
+ RavenwoodRule.setAndroidLogTags("*:S");
+
+ assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT));
+ assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT));
+ }
+
+ @Test
+ public void testComplex() {
+ RavenwoodRule.setAndroidLogTags("TAG1:W TAG2:D *:I");
+
+ assertBarelyLoggable("TAG1", Log.WARN);
+ assertBarelyLoggable("TAG2", Log.DEBUG);
+ assertBarelyLoggable("TAG3", Log.INFO);
+ }
+
+ @Test
+ public void testAllVerbose_setLogLevel() {
+ RavenwoodRule.setAndroidLogTags(null);
+ RavenwoodRule.setLogLevel(null, Log.VERBOSE);
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+ assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+ assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+ }
+
+ @Test
+ public void testAllSilent_setLogLevel() {
+ RavenwoodRule.setAndroidLogTags(null);
+ RavenwoodRule.setLogLevel(null, Log.ASSERT + 1);
+
+ assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT));
+ assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT));
+ }
+
+ @Test
+ public void testComplex_setLogLevel() {
+ RavenwoodRule.setAndroidLogTags(null);
+ RavenwoodRule.setLogLevel(null, Log.INFO);
+ RavenwoodRule.setLogLevel("TAG1", Log.WARN);
+ RavenwoodRule.setLogLevel("TAG2", Log.DEBUG);
+
+ assertBarelyLoggable("TAG1", Log.WARN);
+ assertBarelyLoggable("TAG2", Log.DEBUG);
+ assertBarelyLoggable("TAG3", Log.INFO);
+ }
+}
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
index 08f5397730da..83c31512eb70 100644
--- a/ravenwood/texts/ravenwood-common-policies.txt
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -14,7 +14,7 @@ class :r keepclass
# Support APIs not available in standard JRE
class java.io.FileDescriptor keep
- method getInt$ ()I @com.android.ravenwood.RavenwoodJdkPatch.getInt$
- method setInt$ (I)V @com.android.ravenwood.RavenwoodJdkPatch.setInt$
+ method getInt$ @com.android.ravenwood.RavenwoodJdkPatch.getInt$
+ method setInt$ @com.android.ravenwood.RavenwoodJdkPatch.setInt$
class java.util.LinkedHashMap keep
- method eldest ()Ljava/util/Map$Entry; @com.android.ravenwood.RavenwoodJdkPatch.eldest
+ method eldest @com.android.ravenwood.RavenwoodJdkPatch.eldest
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index 59fa464a7212..fc885d6f463b 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -66,6 +66,9 @@ class InMemoryOutputFilter(
methodName: String,
descriptor: String
) {
+ if (descriptor == "*") {
+ return
+ }
if (classes.findMethod(className, methodName, descriptor) == null) {
log.w("Unknown method $className.$methodName$descriptor")
}
@@ -92,7 +95,8 @@ class InMemoryOutputFilter(
descriptor: String,
): FilterPolicyWithReason {
return mPolicies[getMethodKey(className, methodName, descriptor)]
- ?: super.getPolicyForMethod(className, methodName, descriptor)
+ ?: mPolicies[getMethodKey(className, methodName, "*")]
+ ?: super.getPolicyForMethod(className, methodName, descriptor)
}
fun setPolicyForMethod(
@@ -107,7 +111,8 @@ class InMemoryOutputFilter(
override fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
return mRenames[getMethodKey(className, methodName, descriptor)]
- ?: super.getRenameTo(className, methodName, descriptor)
+ ?: mRenames[getMethodKey(className, methodName, "*")]
+ ?: super.getRenameTo(className, methodName, descriptor)
}
fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index caf80ebec0c9..7462a8ce12c5 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -303,12 +303,21 @@ class TextFileFilterPolicyParser(
}
private fun parseMethod(fields: Array<String>) {
- if (fields.size < 4) {
- throw ParseException("Method ('m') expects 3 fields.")
+ if (fields.size < 3 || fields.size > 4) {
+ throw ParseException("Method ('m') expects 3 or 4 fields.")
}
val name = fields[1]
- val signature = fields[2]
- val policy = parsePolicy(fields[3])
+ val signature: String
+ val policyStr: String
+ if (fields.size <= 3) {
+ signature = "*"
+ policyStr = fields[2]
+ } else {
+ signature = fields[2]
+ policyStr = fields[3]
+ }
+
+ val policy = parsePolicy(policyStr)
if (!policy.isUsableWithMethods) {
throw ParseException("Method can't have policy '$policy'")
@@ -321,7 +330,7 @@ class TextFileFilterPolicyParser(
policy.withReason(FILTER_REASON)
)
if (policy == FilterPolicy.Substitute) {
- val fromName = fields[3].substring(1)
+ val fromName = policyStr.substring(1)
if (fromName == name) {
throw ParseException(
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
index d45f41407a52..a3f934cacc2c 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
@@ -48,10 +48,11 @@ class TextFilePolicyMethodReplaceFilter(
// Maybe use 'Tri' if we end up having too many replacements.
spec.forEach {
if (className == it.fromClass &&
- methodName == it.fromMethod &&
- descriptor == it.fromDescriptor
+ methodName == it.fromMethod
) {
- return MethodReplaceTarget(it.toClass, it.toMethod)
+ if (it.fromDescriptor == "*" || descriptor == it.fromDescriptor) {
+ return MethodReplaceTarget(it.toClass, it.toMethod)
+ }
}
}
return null
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 3c138d21b75d..2f35d35d608d 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -3,11 +3,11 @@ class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy keep
# field remove remove # Implicitly remove
method <init> ()V keep
method addOne (I)I keep
- method addOneInner (I)I keep
+ method addOneInner keep
method toBeRemoved (Ljava/lang/String;)V remove
method addTwo (I)I @addTwo_host
# method addTwo_host (I)I # used as a substitute
- method nativeAddThree (I)I @addThree_host
+ method nativeAddThree @addThree_host
# method addThree_host (I)I # used as a substitute
method unsupportedMethod ()Ljava/lang/String; throw
method visibleButUsesUnsupportedMethod ()Ljava/lang/String; keep
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index a0b989b44f4f..ad87ceaf6f38 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -86,10 +86,17 @@ flag {
}
flag {
- name: "enable_hardware_shortcut_disables_warning"
- namespace: "accessibility"
- description: "When the user purposely enables the hardware shortcut, preemptively disables the first-time warning message."
- bug: "287065325"
+ name: "enable_hardware_shortcut_disables_warning"
+ namespace: "accessibility"
+ description: "When the user purposely enables the hardware shortcut, preemptively disables the first-time warning message."
+ bug: "287065325"
+}
+
+flag {
+ name: "enable_low_vision_hats"
+ namespace: "accessibility"
+ description: "Use HaTS for low vision feedback."
+ bug: "380346799"
}
flag {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ab556b39f516..e50535fb5e53 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1028,8 +1028,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_SETTING_RESTORED);
- Handler receiverHandler =
- Flags.managerAvoidReceiverTimeout() ? BackgroundThread.getHandler() : null;
+ Handler receiverHandler = BackgroundThread.getHandler();
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -6648,28 +6647,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId);
- if (Flags.managerPackageMonitorLogicFix()) {
- if (!doit) {
- // if we're not handling the stop here, then we only need to know
- // if any of the force-stopped packages are currently enabled.
- return userState.mEnabledServices.stream().anyMatch(
- (comp) -> Arrays.stream(packages).anyMatch(
- (pkg) -> pkg.equals(comp.getPackageName()))
- );
- } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
- mManagerService.onUserStateChangedLocked(userState);
- }
- return false;
- } else {
- // this old logic did not properly indicate when base packageMonitor's routine
- // should handle stopping the package.
- if (doit && mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
- mManagerService.onUserStateChangedLocked(userState);
- return false;
- } else {
- return true;
- }
+ if (!doit) {
+ // if we're not handling the stop here, then we only need to know
+ // if any of the force-stopped packages are currently enabled.
+ return userState.mEnabledServices.stream().anyMatch(
+ (comp) -> Arrays.stream(packages).anyMatch(
+ (pkg) -> pkg.equals(comp.getPackageName()))
+ );
+ } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
+ mManagerService.onUserStateChangedLocked(userState);
}
+ return false;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index f45fa921c4a2..5ae077363c88 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -405,10 +405,9 @@ public class AccessibilitySecurityPolicy {
* @throws SecurityException if the input method is not in the same package as the service.
*/
@AccessibilityService.SoftKeyboardController.EnableImeResult
- int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service)
- throws SecurityException {
+ int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service,
+ int callingUserId) throws SecurityException {
final String servicePackageName = service.getComponentName().getPackageName();
- final int callingUserId = UserHandle.getCallingUserId();
InputMethodInfo inputMethodInfo = null;
List<InputMethodInfo> inputMethodInfoList =
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 15999d19ebc0..a3fe9ec5ea22 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -410,9 +410,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
final @AccessibilityService.SoftKeyboardController.EnableImeResult int checkResult;
final long identity = Binder.clearCallingIdentity();
try {
- synchronized (mLock) {
- checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this);
- }
+ checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this, callingUserId);
if (checkResult != ENABLE_IME_SUCCESS) {
return checkResult;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index da11a76d5282..f8551457d04d 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -183,14 +183,12 @@ public class ProxyManager {
synchronized (mLock) {
mProxyA11yServiceConnections.put(displayId, connection);
- if (Flags.proxyUseAppsOnVirtualDeviceListener()) {
- if (mAppsOnVirtualDeviceListener == null) {
- mAppsOnVirtualDeviceListener = allRunningUids ->
- notifyProxyOfRunningAppsChange(allRunningUids);
- final VirtualDeviceManagerInternal localVdm = getLocalVdm();
- if (localVdm != null) {
- localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
- }
+ if (mAppsOnVirtualDeviceListener == null) {
+ mAppsOnVirtualDeviceListener = allRunningUids ->
+ notifyProxyOfRunningAppsChange(allRunningUids);
+ final VirtualDeviceManagerInternal localVdm = getLocalVdm();
+ if (localVdm != null) {
+ localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
}
}
if (mProxyA11yServiceConnections.size() == 1) {
@@ -331,14 +329,12 @@ public class ProxyManager {
// device.
if (!isProxyedDeviceId(deviceId)) {
synchronized (mLock) {
- if (Flags.proxyUseAppsOnVirtualDeviceListener()) {
- if (mProxyA11yServiceConnections.size() == 0) {
- final VirtualDeviceManagerInternal localVdm = getLocalVdm();
- if (localVdm != null && mAppsOnVirtualDeviceListener != null) {
- localVdm.unregisterAppsOnVirtualDeviceListener(
- mAppsOnVirtualDeviceListener);
- mAppsOnVirtualDeviceListener = null;
- }
+ if (mProxyA11yServiceConnections.size() == 0) {
+ final VirtualDeviceManagerInternal localVdm = getLocalVdm();
+ if (localVdm != null && mAppsOnVirtualDeviceListener != null) {
+ localVdm.unregisterAppsOnVirtualDeviceListener(
+ mAppsOnVirtualDeviceListener);
+ mAppsOnVirtualDeviceListener = null;
}
}
mSystemSupport.removeDeviceIdLocked(deviceId);
@@ -671,8 +667,7 @@ public class ProxyManager {
+ getLastSentStateLocked(deviceId));
Slog.v(LOG_TAG, "force update: " + forceUpdate);
}
- if ((getLastSentStateLocked(deviceId)) != proxyState
- || (Flags.proxyUseAppsOnVirtualDeviceListener() && forceUpdate)) {
+ if ((getLastSentStateLocked(deviceId)) != proxyState || forceUpdate) {
setLastStateLocked(deviceId, proxyState);
mMainHandler.post(() -> {
synchronized (mLock) {
@@ -873,33 +868,22 @@ public class ProxyManager {
for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) {
final AccessibilityManagerService.Client client =
((AccessibilityManagerService.Client) clients.getRegisteredCallbackCookie(i));
- if (Flags.proxyUseAppsOnVirtualDeviceListener()) {
- if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) {
- continue;
- }
- boolean uidBelongsToDevice =
- localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId);
- if (client.mDeviceId != deviceId && uidBelongsToDevice) {
- if (DEBUG) {
- Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
- + Arrays.toString(client.mPackageNames));
- }
- client.mDeviceId = deviceId;
- } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) {
- client.mDeviceId = DEVICE_ID_DEFAULT;
- if (DEBUG) {
- Slog.v(LOG_TAG, "Packages moved to the default device from device id "
- + deviceId + " are " + Arrays.toString(client.mPackageNames));
- }
+ if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) {
+ continue;
+ }
+ boolean uidBelongsToDevice =
+ localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId);
+ if (client.mDeviceId != deviceId && uidBelongsToDevice) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
+ + Arrays.toString(client.mPackageNames));
}
- } else {
- if (deviceId != DEVICE_ID_DEFAULT && deviceId != DEVICE_ID_INVALID
- && localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId)) {
- if (DEBUG) {
- Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
- + Arrays.toString(client.mPackageNames));
- }
- client.mDeviceId = deviceId;
+ client.mDeviceId = deviceId;
+ } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) {
+ client.mDeviceId = DEVICE_ID_DEFAULT;
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Packages moved to the default device from device id "
+ + deviceId + " are " + Arrays.toString(client.mPackageNames));
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index 6b48d2bacf9d..a4568aaa7a0d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -31,7 +31,6 @@ import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import com.android.internal.R;
-import com.android.server.accessibility.Flags;
/**
* Handles the behavior while receiving scaling and panning gestures if it's enabled.
@@ -73,13 +72,9 @@ class PanningScalingHandler extends
mMaxScale = maxScale;
mMinScale = minScale;
mBlockScroll = blockScroll;
- if (Flags.pinchZoomZeroMinSpan()) {
- mScaleGestureDetector = new ScaleGestureDetector(context,
- ViewConfiguration.get(context).getScaledTouchSlop() * 2,
- /* minSpan= */ 0, Handler.getMain(), this);
- } else {
- mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
- }
+ mScaleGestureDetector = new ScaleGestureDetector(context,
+ ViewConfiguration.get(context).getScaledTouchSlop() * 2,
+ /* minSpan= */ 0, Handler.getMain(), this);
mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
mScaleGestureDetector.setQuickScaleEnabled(false);
mMagnificationDelegate = magnificationDelegate;
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 5d2ef770b96b..5e1b1473d233 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -23,6 +23,16 @@ flag {
}
flag {
+ name: "relayout_fix"
+ namespace: "autofill"
+ description: "Fixing relayout issue. Guarding enabling device config flags"
+ bug: "381226145"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "include_invisible_view_group_in_assist_structure"
namespace: "autofill"
description: "Mitigation for autofill providers miscalculating view visibility"
diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
index 02f186557ca4..6ced096e8778 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
@@ -101,11 +101,16 @@ public class BackupAgentConnectionManager {
private static final class BackupAgentConnection {
public final ApplicationInfo appInfo;
+ public final int backupMode;
+ public final boolean inRestrictedMode;
public IBackupAgent backupAgent;
public boolean connecting = true; // Assume we are trying to connect on creation.
- private BackupAgentConnection(ApplicationInfo appInfo) {
+ private BackupAgentConnection(ApplicationInfo appInfo, int backupMode,
+ boolean inRestrictedMode) {
this.appInfo = appInfo;
+ this.backupMode = backupMode;
+ this.inRestrictedMode = inRestrictedMode;
}
}
@@ -113,26 +118,31 @@ public class BackupAgentConnectionManager {
* Fires off a backup agent, blocking until it attaches (i.e. ActivityManager calls
* {@link #agentConnected(String, IBinder)}) or until this operation times out.
*
- * @param mode a {@code BACKUP_MODE} from {@link android.app.ApplicationThreadConstants}.
+ * @param backupMode a {@code BACKUP_MODE} from {@link android.app.ApplicationThreadConstants}.
*/
@Nullable
- public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode,
+ public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int backupMode,
@BackupAnnotations.BackupDestination int backupDestination) {
+ if (app == null) {
+ Slog.w(TAG, mUserIdMsg + "bindToAgentSynchronous for null app");
+ return null;
+ }
+
synchronized (mAgentConnectLock) {
- boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(mode,
+ boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(backupMode,
app.packageName);
if (mCurrentConnection != null) {
Slog.e(TAG, mUserIdMsg + "binding to new agent before unbinding from old one: "
+ mCurrentConnection.appInfo.packageName);
}
- mCurrentConnection = new BackupAgentConnection(app);
+ mCurrentConnection = new BackupAgentConnection(app, backupMode, useRestrictedMode);
// bindBackupAgent() is an async API. It will kick off the app's process and call
// agentConnected() when it receives the agent from the app.
boolean startedBindSuccessfully = false;
try {
- startedBindSuccessfully = mActivityManager.bindBackupAgent(app.packageName, mode,
- mUserId, backupDestination, useRestrictedMode);
+ startedBindSuccessfully = mActivityManager.bindBackupAgent(app.packageName,
+ backupMode, mUserId, backupDestination, useRestrictedMode);
} catch (RemoteException e) {
// can't happen - ActivityManager is local
}
@@ -173,28 +183,66 @@ public class BackupAgentConnectionManager {
/**
* Tell the ActivityManager that we are done with the {@link IBackupAgent} of this {@code app}.
* It will tell the app to destroy the agent.
+ *
+ * <p>If {@code allowKill} is set, this will kill the app's process if the app is in restricted
+ * mode or if it was started for restore and specified {@code android:killAfterRestore} in its
+ * manifest.
+ *
+ * @see #shouldUseRestrictedBackupModeForPackage(int, String)
*/
- public void unbindAgent(ApplicationInfo app) {
- synchronized (mAgentConnectLock) {
- if (mCurrentConnection == null) {
- Slog.w(TAG, mUserIdMsg + "unbindAgent but no current connection");
- } else if (!mCurrentConnection.appInfo.packageName.equals(app.packageName)) {
- Slog.w(TAG, mUserIdMsg + "unbindAgent for unexpected package: " + app.packageName
- + " expected: " + mCurrentConnection.appInfo.packageName);
- } else {
- mCurrentConnection = null;
- }
+ public void unbindAgent(ApplicationInfo app, boolean allowKill) {
+ if (app == null) {
+ Slog.w(TAG, mUserIdMsg + "unbindAgent for null app");
+ return;
+ }
+ synchronized (mAgentConnectLock) {
// Even if we weren't expecting to be bound to this agent, we should still call
// ActivityManager just in case. It will ignore the call if it also wasn't expecting it.
try {
mActivityManager.unbindBackupAgent(app);
+
+ // Evaluate this before potentially setting mCurrentConnection = null.
+ boolean willKill = allowKill && shouldKillAppOnUnbind(app);
+
+ if (mCurrentConnection == null) {
+ Slog.w(TAG, mUserIdMsg + "unbindAgent but no current connection");
+ } else if (!mCurrentConnection.appInfo.packageName.equals(app.packageName)) {
+ Slog.w(TAG,
+ mUserIdMsg + "unbindAgent for unexpected package: " + app.packageName
+ + " expected: " + mCurrentConnection.appInfo.packageName);
+ } else {
+ mCurrentConnection = null;
+ }
+
+ if (willKill) {
+ Slog.i(TAG, mUserIdMsg + "Killing agent host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ }
} catch (RemoteException e) {
// Can't happen - activity manager is local
}
}
}
+ @GuardedBy("mAgentConnectLock")
+ private boolean shouldKillAppOnUnbind(ApplicationInfo app) {
+ // We don't ask system UID processes to be killed.
+ if (UserHandle.isCore(app.uid)) {
+ return false;
+ }
+
+ // If the app is in restricted mode or if we're not sure if it is because our internal
+ // state is messed up, we need to avoid it being stuck in it.
+ if (mCurrentConnection == null || mCurrentConnection.inRestrictedMode) {
+ return true;
+ }
+
+ // App was doing restore and asked to be killed afterwards.
+ return isBackupModeRestore(mCurrentConnection.backupMode)
+ && (app.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0;
+ }
+
/**
* Callback: a requested backup agent has been instantiated. This should only be called from
* the {@link ActivityManager} when it's telling us that an agent is ready after a call to
@@ -307,16 +355,16 @@ public class BackupAgentConnectionManager {
*/
private boolean shouldUseRestrictedBackupModeForPackage(
@BackupAnnotations.OperationType int mode, String packageName) {
- if (!Flags.enableRestrictedModeChanges()) {
- return true;
- }
-
// Key/Value apps are never put in restricted mode.
if (mode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|| mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE) {
return false;
}
+ if (!Flags.enableRestrictedModeChanges()) {
+ return true;
+ }
+
try {
PackageManager.Property property = mPackageManager.getPropertyAsUser(
PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE,
@@ -352,6 +400,11 @@ public class BackupAgentConnectionManager {
return true;
}
+ private static boolean isBackupModeRestore(int backupMode) {
+ return backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE
+ || backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL;
+ }
+
@VisibleForTesting
Thread getThreadForCancellation(Runnable operation) {
return new Thread(operation, /* operationName */ "agent-disconnected");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 136bacdd6399..b343ec8e709b 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -300,7 +300,8 @@ public class KeyValueAdbBackupEngine {
}
private void cleanup() {
- mBackupManagerService.tearDownAgentAndKill(mCurrentPackage.applicationInfo);
+ mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mCurrentPackage.applicationInfo, /* allowKill= */ true);
mBlankStateName.delete();
mNewStateName.delete();
mBackupDataName.delete();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e085f6e63067..a90b693c5a1d 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1998,39 +1998,6 @@ public class UserBackupManagerService {
return mOperationStorage.isBackupOperationInProgress();
}
- /** Unbind the backup agent and kill the app if it's a non-system app. */
- public void tearDownAgentAndKill(ApplicationInfo app) {
- if (app == null) {
- // Null means the system package, so just quietly move on. :)
- return;
- }
-
- try {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(app);
-
- // The agent was running with a stub Application object, so shut it down.
- // !!! We hardcode the confirmation UI's package name here rather than use a
- // manifest flag! TODO something less direct.
- if (!UserHandle.isCore(app.uid)
- && !app.packageName.equals("com.android.backupconfirm")) {
- if (MORE_DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Killing agent host process"));
- }
- mActivityManager.killApplicationProcess(app.processName, app.uid);
- } else {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Not killing after operation: " + app.processName));
- }
- }
- } catch (RemoteException e) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Lost app trying to shut down"));
- }
- }
-
// ----- Full-data backup scheduling -----
/**
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index b98cb1086680..cf617a523bec 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -323,7 +323,8 @@ public class FullBackupEngine {
private void tearDown() {
if (mPkg != null) {
- backupManagerService.tearDownAgentAndKill(mPkg.applicationInfo);
+ backupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mPkg.applicationInfo, /* allowKill= */ true);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index dc6709141b25..0ba0d710af38 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -503,7 +503,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
Slog.w(TAG, "adb backup cancel of " + target);
}
if (target != null) {
- mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ target.applicationInfo, /* allowKill= */ true);
}
mOperationStorage.removeOperation(mCurrentOpToken);
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 65730c9591a8..799494831f19 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -596,8 +596,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
// from the preflight pass. If we got as far as preflight, we now need
// to tear down the target process.
if (mBackupRunner != null) {
- mUserBackupManagerService.tearDownAgentAndKill(
- currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
}
// ... and continue looping.
} else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
@@ -609,7 +609,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
packageName);
}
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
BackupObserverUtils
@@ -617,7 +618,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
BackupManager.ERROR_AGENT_FAILURE);
Slog.w(TAG, "Application failure for package: " + packageName);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) {
BackupObserverUtils
@@ -626,7 +628,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
Slog.w(TAG, "Backup cancelled. package=" + packageName +
", cancelAll=" + mCancelAll);
EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
BackupObserverUtils
@@ -636,7 +639,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
// Abort entire backup pass.
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
return;
} else {
// Success!
@@ -1005,7 +1009,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mIsCancelled = true;
// Cancel tasks spun off by this task.
mUserBackupManagerService.handleCancel(mEphemeralToken, cancelAll);
- mUserBackupManagerService.tearDownAgentAndKill(mTarget.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mTarget.applicationInfo, /* allowKill= */ true);
// Free up everyone waiting on this task and its children.
mPreflightLatch.countDown();
mBackupLatch.countDown();
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 82232a653858..689c43a1cc96 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -1303,7 +1303,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
// For PM metadata (for which applicationInfo is null) there is no agent-bound state.
if (mCurrentPackage.applicationInfo != null) {
mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
- mCurrentPackage.applicationInfo);
+ mCurrentPackage.applicationInfo, /* allowKill= */ false);
}
mAgent = null;
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b59e860f81fe..237ffa3bdb21 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -727,7 +727,8 @@ public class FullRestoreEngine extends RestoreEngine {
latch.await();
}
- mBackupManagerService.tearDownAgentAndKill(app);
+ mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ app, /* allowKill= */ true);
} catch (RemoteException e) {
Slog.d(TAG, "Lost app trying to shut down");
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e5c7e5cce757..dad84c86deef 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -51,7 +51,6 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.EventLog;
@@ -1487,49 +1486,10 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// If this wasn't the PM pseudopackage, tear down the agent side
if (mCurrentPackage.applicationInfo != null) {
- // unbind and tidy up even on timeout or failure
- try {
- backupManagerService
- .getActivityManager()
- .unbindBackupAgent(mCurrentPackage.applicationInfo);
-
- // The agent was probably running with a stub Application object,
- // which isn't a valid run mode for the main app logic. Shut
- // down the app so that next time it's launched, it gets the
- // usual full initialization. Note that this is only done for
- // full-system restores: when a single app has requested a restore,
- // it is explicitly not killed following that operation.
- //
- // We execute this kill when these conditions hold:
- // 1. it's not a system-uid process,
- // 2. the app did not request its own restore (mTargetPackage == null), and
- // either
- // 3a. the app is a full-data target (TYPE_FULL_STREAM) or
- // b. the app does not state android:killAfterRestore="false" in its manifest
- final int appFlags = mCurrentPackage.applicationInfo.flags;
- final boolean killAfterRestore =
- !UserHandle.isCore(mCurrentPackage.applicationInfo.uid)
- && ((mRestoreDescription.getDataType()
- == RestoreDescription.TYPE_FULL_STREAM)
- || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
- != 0));
-
- if (mTargetPackage == null && killAfterRestore) {
- if (DEBUG) {
- Slog.d(
- TAG,
- "Restore complete, killing host process of "
- + mCurrentPackage.applicationInfo.processName);
- }
- backupManagerService
- .getActivityManager()
- .killApplicationProcess(
- mCurrentPackage.applicationInfo.processName,
- mCurrentPackage.applicationInfo.uid);
- }
- } catch (RemoteException e) {
- // can't happen; we run in the same process as the activity manager
- }
+ // If mTargetPackage is not null it means the app requested its own restore, in which
+ // case we won't allow the app to be killed.
+ backupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mCurrentPackage.applicationInfo, /* allowKill= */ mTargetPackage == null);
}
// The caller is responsible for reestablishing the state machine; our
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
index 7a2106bb7753..4bcfb33b9e7c 100644
--- a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -240,9 +240,10 @@ class BackupRestoreProcessor {
boolean matchesMacAddress = Objects.equals(
associationInfo.getDeviceMacAddress(),
restored.getDeviceMacAddress());
- boolean matchesTag = !Flags.associationTag()
- || Objects.equals(associationInfo.getTag(), restored.getTag());
- return matchesMacAddress && matchesTag;
+ boolean matchesDeviceId = !Flags.associationTag()
+ || (associationInfo.getDeviceId() != null
+ && associationInfo.getDeviceId().isSameDevice(restored.getDeviceId()));
+ return matchesMacAddress || matchesDeviceId;
};
AssociationInfo local = CollectionUtils.find(localAssociations, isSameDevice);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 7cba9e0ccca8..418f3a18688b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -55,6 +55,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
+import android.companion.DeviceId;
import android.companion.IAssociationRequestCallback;
import android.companion.ICompanionDeviceManager;
import android.companion.IOnAssociationsChangedListener;
@@ -318,7 +319,6 @@ public class CompanionDeviceManagerService extends SystemService {
public List<AssociationInfo> getAssociations(String packageName, int userId) {
enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
"get associations");
-
return mAssociationStore.getActiveAssociationsByPackage(userId, packageName);
}
@@ -694,14 +694,10 @@ public class CompanionDeviceManagerService extends SystemService {
}
@Override
- public void setAssociationTag(int associationId, String tag) {
- mAssociationRequestsProcessor.setAssociationTag(associationId, tag);
+ public void setDeviceId(int associationId, DeviceId deviceId) {
+ mAssociationRequestsProcessor.setDeviceId(associationId, deviceId);
}
- @Override
- public void clearAssociationTag(int associationId) {
- setAssociationTag(associationId, null);
- }
@Override
public byte[] getBackupPayload(int userId) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index 77b17803d434..f2d019bde703 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -38,6 +38,7 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
+import android.companion.DeviceId;
import android.graphics.drawable.Icon;
import android.net.MacAddress;
import android.os.Environment;
@@ -131,8 +132,11 @@ import java.util.concurrent.ConcurrentMap;
* revoked="false"
* last_time_connected="1634641160229"
* time_approved="1634389553216"
- * system_data_sync_flags="0"/>
- *
+ * system_data_sync_flags="0"
+ * device_icon="device_icon">
+ * <device_id
+ * custom_device_id="1234"/>
+ * </association>
* <association
* id="3"
* profile="android.app.role.COMPANION_DEVICE_WATCH"
@@ -143,7 +147,11 @@ import java.util.concurrent.ConcurrentMap;
* revoked="false"
* last_time_connected="1634641160229"
* time_approved="1634641160229"
- * system_data_sync_flags="1"/>
+ * system_data_sync_flags="1"
+ * device_icon="device_icon">
+ * <device_id
+ * custom_device_id="1234"/>
+ * </association>
* </associations>
* </state>
* }</pre>
@@ -160,7 +168,7 @@ public final class AssociationDiskStore {
private static final String XML_TAG_STATE = "state";
private static final String XML_TAG_ASSOCIATIONS = "associations";
private static final String XML_TAG_ASSOCIATION = "association";
- private static final String XML_TAG_TAG = "tag";
+ private static final String XML_TAG_DEVICE_ID = "device_id";
private static final String XML_ATTR_PERSISTENCE_VERSION = "persistence-version";
private static final String XML_ATTR_MAX_ID = "max-id";
@@ -177,6 +185,8 @@ public final class AssociationDiskStore {
private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
private static final String XML_ATTR_SYSTEM_DATA_SYNC_FLAGS = "system_data_sync_flags";
private static final String XML_ATTR_DEVICE_ICON = "device_icon";
+ private static final String XML_ATTR_CUSTOM_DEVICE_ID = "custom_device_id";
+ private static final String XML_ATTR_MAC_ADDRESS_DEVICE_ID = "mac_address_device_id";
private static final String LEGACY_XML_ATTR_DEVICE = "device";
@@ -389,16 +399,16 @@ public final class AssociationDiskStore {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
- final String tag = readStringAttribute(parser, XML_TAG_TAG);
final String deviceAddress = readStringAttribute(parser, LEGACY_XML_ATTR_DEVICE);
final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
- return new AssociationInfo(associationId, userId, appPackage, tag,
+ return new AssociationInfo(associationId, userId, appPackage,
MacAddress.fromString(deviceAddress), null, profile, null,
/* managedByCompanionApp */ false, notify, /* revoked */ false, /* pending */ false,
- timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, null);
+ timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, /* deviceIcon */ null,
+ /* deviceId */ null);
}
private static Associations readAssociationsV1(@NonNull TypedXmlPullParser parser,
@@ -436,7 +446,6 @@ public final class AssociationDiskStore {
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
- final String tag = readStringAttribute(parser, XML_TAG_TAG);
final MacAddress macAddress = stringToMacAddress(
readStringAttribute(parser, XML_ATTR_MAC_ADDRESS));
final String displayName = readStringAttribute(parser, XML_ATTR_DISPLAY_NAME);
@@ -451,10 +460,26 @@ public final class AssociationDiskStore {
XML_ATTR_SYSTEM_DATA_SYNC_FLAGS, 0);
final Icon deviceIcon = byteArrayToIcon(
readByteArrayAttribute(parser, XML_ATTR_DEVICE_ICON));
+ parser.nextTag();
+ final DeviceId deviceId = readDeviceId(parser);
- return new AssociationInfo(associationId, userId, appPackage, tag, macAddress, displayName,
+ return new AssociationInfo(associationId, userId, appPackage, macAddress, displayName,
profile, null, selfManaged, notify, revoked, pending, timeApproved,
- lastTimeConnected, systemDataSyncFlags, deviceIcon);
+ lastTimeConnected, systemDataSyncFlags, deviceIcon, deviceId);
+ }
+
+ private static DeviceId readDeviceId(@NonNull TypedXmlPullParser parser)
+ throws XmlPullParserException {
+ if (isStartOfTag(parser, XML_TAG_DEVICE_ID)) {
+ final String customDeviceId = readStringAttribute(
+ parser, XML_ATTR_CUSTOM_DEVICE_ID);
+ final MacAddress macAddress = stringToMacAddress(
+ readStringAttribute(parser, XML_ATTR_MAC_ADDRESS_DEVICE_ID));
+
+ return new DeviceId(customDeviceId, macAddress);
+ }
+
+ return null;
}
private static void writeAssociations(@NonNull XmlSerializer parent,
@@ -475,7 +500,6 @@ public final class AssociationDiskStore {
writeIntAttribute(serializer, XML_ATTR_ID, a.getId());
writeStringAttribute(serializer, XML_ATTR_PROFILE, a.getDeviceProfile());
writeStringAttribute(serializer, XML_ATTR_PACKAGE, a.getPackageName());
- writeStringAttribute(serializer, XML_TAG_TAG, a.getTag());
writeStringAttribute(serializer, XML_ATTR_MAC_ADDRESS, a.getDeviceMacAddressAsString());
writeStringAttribute(serializer, XML_ATTR_DISPLAY_NAME, a.getDisplayName());
writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
@@ -490,14 +514,33 @@ public final class AssociationDiskStore {
writeByteArrayAttribute(
serializer, XML_ATTR_DEVICE_ICON, iconToByteArray(a.getDeviceIcon()));
+ writeDeviceId(serializer, a);
serializer.endTag(null, XML_TAG_ASSOCIATION);
}
+ private static void writeDeviceId(XmlSerializer parent, @NonNull AssociationInfo a)
+ throws IOException {
+ if (a.getDeviceId() != null) {
+ final XmlSerializer serializer = parent.startTag(null, XML_TAG_DEVICE_ID);
+ writeStringAttribute(
+ serializer,
+ XML_ATTR_CUSTOM_DEVICE_ID,
+ a.getDeviceId().getCustomId()
+ );
+ writeStringAttribute(
+ serializer,
+ XML_ATTR_MAC_ADDRESS_DEVICE_ID,
+ a.getDeviceId().getMacAddressAsString()
+ );
+ serializer.endTag(null, XML_TAG_DEVICE_ID);
+ }
+ }
+
private static void requireStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
throws XmlPullParserException {
if (isStartOfTag(parser, tag)) return;
throw new XmlPullParserException(
- "Should be at the start of \"" + XML_TAG_ASSOCIATIONS + "\" tag");
+ "Should be at the start of \"" + tag + "\" tag");
}
private static @Nullable MacAddress stringToMacAddress(@Nullable String address) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index aebd11a7f582..899b302316f9 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -41,6 +41,7 @@ import android.app.PendingIntent;
import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
+import android.companion.DeviceId;
import android.companion.Flags;
import android.companion.IAssociationRequestCallback;
import android.content.ComponentName;
@@ -298,10 +299,10 @@ public class AssociationRequestsProcessor {
final long timestamp = System.currentTimeMillis();
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
- /* tag */ null, macAddress, displayName, deviceProfile, associatedDevice,
+ macAddress, displayName, deviceProfile, associatedDevice,
selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false,
/* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0,
- deviceIcon);
+ deviceIcon, /* deviceId */ null);
// Add role holder for association (if specified) and add new association to store.
maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver);
}
@@ -354,14 +355,14 @@ public class AssociationRequestsProcessor {
}
/**
- * Set association tag.
+ * Set Device id for the association.
*/
- public void setAssociationTag(int associationId, String tag) {
- Slog.i(TAG, "Setting association tag=[" + tag + "] to id=[" + associationId + "]...");
+ public void setDeviceId(int associationId, DeviceId deviceId) {
+ Slog.i(TAG, "Setting DeviceId=[" + deviceId + "] to id=[" + associationId + "]...");
AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
associationId);
- association = (new AssociationInfo.Builder(association)).setTag(tag).build();
+ association = (new AssociationInfo.Builder(association)).setDeviceId(deviceId).build();
mAssociationStore.updateAssociation(association);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6d60164e3d0e..08206150cebb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -178,6 +178,7 @@ java_library_static {
static_libs: [
"android.frameworks.vibrator-V1-java", // AIDL
+ "android.frameworks.devicestate-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
@@ -242,6 +243,7 @@ java_library_static {
"powerstats_flags_lib",
"locksettings_flags_lib",
"profiling_flags_lib",
+ "android.adpf.sessionmanager_aidl-java",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/SecurityStateManagerService.java b/services/core/java/com/android/server/SecurityStateManagerService.java
index 98039be20897..fe21fbda7130 100644
--- a/services/core/java/com/android/server/SecurityStateManagerService.java
+++ b/services/core/java/com/android/server/SecurityStateManagerService.java
@@ -22,6 +22,7 @@ import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.ISecurityStateManager;
@@ -56,6 +57,15 @@ public class SecurityStateManagerService extends ISecurityStateManager.Stub {
@Override
public Bundle getGlobalSecurityState() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getGlobalSecurityStateInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private Bundle getGlobalSecurityStateInternal() {
Bundle globalSecurityState = new Bundle();
globalSecurityState.putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH);
globalSecurityState.putString(KEY_VENDOR_SPL,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7eca893a7035..37d0c7de7629 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -132,7 +132,7 @@ import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.security.Flags.preventIntentRedirect;
import static android.security.Flags.preventIntentRedirectCollectNestedKeysOnServerIfNotCollected;
-import static android.security.Flags.preventIntentRedirectShowToastIfNestedKeysNotCollected;
+import static android.security.Flags.preventIntentRedirectShowToastIfNestedKeysNotCollectedRW;
import static android.security.Flags.preventIntentRedirectThrowExceptionIfNestedKeysNotCollected;
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
@@ -19339,7 +19339,7 @@ public class ActivityManagerService extends IActivityManager.Stub
"[IntentRedirect] The intent does not have its nested keys collected as a "
+ "preparation for creating intent creator tokens. Intent: "
+ intent + "; creatorPackage: " + creatorPackage);
- if (preventIntentRedirectShowToastIfNestedKeysNotCollected()) {
+ if (preventIntentRedirectShowToastIfNestedKeysNotCollectedRW()) {
UiThread.getHandler().post(
() -> Toast.makeText(mContext,
"Nested keys not collected. go/report-bug-intentRedir to report a"
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e838a8dc7e7f..d0153d87ad78 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -139,6 +139,11 @@ public class SettingsToPropertiesMapper {
// The list is sorted.
@VisibleForTesting
static final String[] sDeviceConfigAconfigScopes = new String[] {
+ "tv_os",
+ "aaos_carframework_triage",
+ "aaos_performance_triage",
+ "aaos_user_triage",
+ "aaos_window_triage",
"aaos_audio_triage",
"aaos_power_triage",
"aaos_sdv",
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c6317bd29824..2f7a54daed27 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9554,9 +9554,11 @@ public class AudioService extends IAudioService.Stub
index = (mIndexMap.valueAt(i) + 5)/10;
}
- sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION,
- SENDMSG_REPLACE, device, isAbsoluteVolume ? 1 : 0, this,
- /*delay=*/0);
+ if (mStreamType == AudioSystem.STREAM_MUSIC) {
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION,
+ SENDMSG_QUEUE, device, isAbsoluteVolume ? 1 : 0, this,
+ /*delay=*/0);
+ }
setStreamVolumeIndex(index, device);
}
@@ -10103,7 +10105,7 @@ public class AudioService extends IAudioService.Stub
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
- sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_REPLACE,
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_QUEUE,
device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device) ? 1 : 0),
streamState, /*delay=*/0);
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 995e16bd8f17..1b5f0e5d31c8 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -657,8 +657,8 @@ public class AudioServiceEvents {
return "CSD lowering volume to RS1";
case UPDATE_ABS_VOLUME_ATTENUATION:
return String.format(java.util.Locale.US,
- "Updating CSD absolute volume attenuation on device %d with %.2f dB ",
- mLongValue, mFloatValue);
+ "Updating CSD absolute volume attenuation on device 0x%s with %.2f dB ",
+ Long.toHexString(mLongValue), mFloatValue);
}
return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8b9c664a31fd..251344395ae3 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,16 @@ package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
@@ -44,6 +53,10 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IProcessObserver;
import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
@@ -56,9 +69,12 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.Trace;
+import android.util.LongSparseLongArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -82,6 +98,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
@@ -130,6 +147,8 @@ public final class DeviceStateManagerService extends SystemService {
@NonNull
private final BinderService mBinderService;
@NonNull
+ private final HalService mHalService;
+ @NonNull
private final OverrideRequestController mOverrideRequestController;
@NonNull
private final DeviceStateProviderListener mDeviceStateProviderListener;
@@ -139,7 +158,7 @@ public final class DeviceStateManagerService extends SystemService {
// All supported device states keyed by identifier.
@GuardedBy("mLock")
- private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
+ private final SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
// The current committed device state. Will be empty until the first device state provided by
// the DeviceStateProvider is committed.
@@ -177,7 +196,7 @@ public final class DeviceStateManagerService extends SystemService {
@GuardedBy("mLock")
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
- private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
+ private final Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
private Set<Integer> mFoldedDeviceStates = new HashSet<>();
@@ -259,6 +278,7 @@ public final class DeviceStateManagerService extends SystemService {
mDeviceStateProviderListener = new DeviceStateProviderListener();
mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
mBinderService = new BinderService();
+ mHalService = new HalService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mDeviceStateNotificationController = new DeviceStateNotificationController(
context, mHandler,
@@ -272,6 +292,10 @@ public final class DeviceStateManagerService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
+ String halServiceName = IDeviceStateService.DESCRIPTOR + "/default";
+ if (ServiceManager.isDeclared(halServiceName)) {
+ publishBinderService(halServiceName, mHalService);
+ }
publishLocalService(DeviceStateManagerInternal.class, new LocalService());
if (!Flags.deviceStatePropertyMigration()) {
@@ -440,6 +464,11 @@ public final class DeviceStateManagerService extends SystemService {
return mBinderService;
}
+ @VisibleForTesting
+ IDeviceStateService getHalBinderService() {
+ return mHalService;
+ }
+
private void updateSupportedStates(DeviceState[] supportedDeviceStates,
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
synchronized (mLock) {
@@ -1282,6 +1311,124 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ private final class HalService extends IDeviceStateService.Stub {
+ private final LongSparseLongArray mPublicProperties = new LongSparseLongArray();
+ public HalService() {
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN);
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN);
+ mPublicProperties.put(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ mPublicProperties.put(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+ FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+ mPublicProperties.put(
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ FEATURE_REAR_DISPLAY);
+ mPublicProperties.put(
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+ FEATURE_DUAL_DISPLAY);
+ }
+
+ private final class HalBinderCallback implements IDeviceStateManagerCallback {
+ private final IDeviceStateListener mListener;
+
+ private HalBinderCallback(@NonNull IDeviceStateListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onDeviceStateInfoChanged(DeviceStateInfo info) throws RemoteException {
+ DeviceStateConfiguration config = new DeviceStateConfiguration();
+ Set<Integer> systemProperties = new HashSet<>(
+ info.currentState.getConfiguration().getSystemProperties());
+ Set<Integer> physicalProperties = new HashSet<>(
+ info.currentState.getConfiguration().getPhysicalProperties());
+ config.deviceProperties = 0;
+ for (Integer prop : systemProperties) {
+ Long publicProperty = mPublicProperties.get(prop);
+ if (publicProperty != null) {
+ config.deviceProperties |= publicProperty.longValue();
+ }
+ }
+ for (Integer prop : physicalProperties) {
+ Long publicProperty = mPublicProperties.get(prop);
+ if (publicProperty != null) {
+ config.deviceProperties |= publicProperty.longValue();
+ }
+ }
+ mListener.onDeviceStateChanged(config);
+ }
+
+ @Override
+ public void onRequestActive(IBinder token) {
+ //No-op
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ //No-op
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mListener.asBinder();
+ }
+ }
+
+ @Override
+ public void registerListener(IDeviceStateListener listener) throws RemoteException {
+ if (listener == null) {
+ throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ HalBinderCallback callback = new HalBinderCallback(listener);
+ DeviceStateInfo info = registerProcess(callingPid, callback);
+ if (info != null) {
+ callback.onDeviceStateInfoChanged(info);
+ }
+ } catch (SecurityException e) {
+ throw new ServiceSpecificException(ErrorCode.ALREADY_EXISTS);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterListener(IDeviceStateListener listener) throws RemoteException {
+ final int callingPid = Binder.getCallingPid();
+
+ synchronized (mLock) {
+ if (mProcessRecords.contains(callingPid)) {
+ mProcessRecords.remove(callingPid);
+ return;
+ }
+ }
+
+ throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return IDeviceStateService.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return IDeviceStateService.HASH;
+ }
+ }
+
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
private final class BinderService extends IDeviceStateManager.Stub {
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index eb76dcba3b85..382c88327523 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -73,9 +73,13 @@ import java.util.regex.Pattern;
* </pre>
* Supported flags:
* <ul>
- * <li><pre>secure</pre>: creates a secure display</li>
- * <li><pre>own_content_only</pre>: only shows this display's own content</li>
- * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+ * <li><code>secure</code>: creates a secure display</li>
+ * <li><code>own_content_only</code>: only shows this display's own content</li>
+ * <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
+ * <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
+ * <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
+ * <li><code>gravity_bottom_left</code>: display the overlay at the bottom left of the screen</li>
* </ul>
* </p><p>
* Example:
@@ -113,6 +117,12 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
"should_show_system_decorations";
+ // Gravity flags to decide where the overlay should be shown.
+ private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
+ private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
+ private static final String GRAVITY_TOP_RIGHT = "gravity_top_right";
+ private static final String GRAVITY_BOTTOM_LEFT = "gravity_bottom_left";
+
private static final int MIN_WIDTH = 100;
private static final int MIN_HEIGHT = 100;
private static final int MAX_WIDTH = 4096;
@@ -237,8 +247,11 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
String name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_overlay_display_name,
number);
- int gravity = chooseOverlayGravity(number);
OverlayFlags flags = OverlayFlags.parseFlags(flagString);
+ int gravity = flags.mGravity;
+ if (flags.mGravity == Gravity.NO_GRAVITY) {
+ gravity = chooseOverlayGravity(number);
+ }
Slog.i(TAG, "Showing overlay display device #" + number
+ ": name=" + name + ", modes=" + Arrays.toString(modes.toArray())
@@ -266,6 +279,16 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
}
}
+ private static int parseOverlayGravity(String overlayGravity) {
+ return switch (overlayGravity) {
+ case GRAVITY_TOP_LEFT -> Gravity.TOP | Gravity.LEFT;
+ case GRAVITY_TOP_RIGHT -> Gravity.TOP | Gravity.RIGHT;
+ case GRAVITY_BOTTOM_RIGHT -> Gravity.BOTTOM | Gravity.RIGHT;
+ case GRAVITY_BOTTOM_LEFT -> Gravity.BOTTOM | Gravity.LEFT;
+ default -> Gravity.NO_GRAVITY;
+ };
+ }
+
private abstract class OverlayDisplayDevice extends DisplayDevice {
private final String mName;
private final float mRefreshRate;
@@ -605,13 +628,17 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
/** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
final boolean mShouldShowSystemDecorations;
+ final int mGravity;
+
OverlayFlags(
boolean secure,
boolean ownContentOnly,
- boolean shouldShowSystemDecorations) {
+ boolean shouldShowSystemDecorations,
+ int gravity) {
mSecure = secure;
mOwnContentOnly = ownContentOnly;
mShouldShowSystemDecorations = shouldShowSystemDecorations;
+ mGravity = gravity;
}
static OverlayFlags parseFlags(@Nullable String flagString) {
@@ -619,24 +646,26 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
return new OverlayFlags(
false /* secure */,
false /* ownContentOnly */,
- false /* shouldShowSystemDecorations */);
+ false /* shouldShowSystemDecorations */,
+ Gravity.NO_GRAVITY);
}
boolean secure = false;
boolean ownContentOnly = false;
boolean shouldShowSystemDecorations = false;
+ int gravity = Gravity.NO_GRAVITY;
for (String flag: flagString.split(FLAG_SPLITTER)) {
if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
secure = true;
- }
- if (OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY.equals(flag)) {
+ } else if (OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY.equals(flag)) {
ownContentOnly = true;
- }
- if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
+ } else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
shouldShowSystemDecorations = true;
+ } else {
+ gravity = parseOverlayGravity(flag);
}
}
- return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations);
+ return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
}
@Override
@@ -645,6 +674,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
.append("secure=").append(mSecure)
.append(", ownContentOnly=").append(mOwnContentOnly)
.append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+ .append(", gravity").append(Gravity.toString(mGravity))
.append("}")
.toString();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
index 2e66fbc16496..102de73374b7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
@@ -265,6 +265,26 @@ public class HdmiCecAtomWriter {
enumLogReason);
}
+ /**
+ * Writes a HdmiPowerStateChangeOnActiveSourceLostToggled atom representing a
+ * HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST setting change.
+ * @param isEnabled Whether the setting is enabled.
+ * @param enumLogReason The event that triggered the log.
+ * @param manufacturerPnpId Manufacturer PNP ID reported in the EDID.
+ * @param manufacturerYear Manufacture year reported in the EDID.
+ * @param manufacturerWeek Manufacture week reporter in the EDID.
+ */
+ public void powerStateChangeOnActiveSourceLostChanged(boolean isEnabled, int enumLogReason,
+ String manufacturerPnpId, int manufacturerYear, int manufacturerWeek) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.HDMI_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLED,
+ isEnabled,
+ enumLogReason,
+ manufacturerPnpId,
+ manufacturerYear,
+ manufacturerWeek);
+ }
+
private int earcStateToEnum(int earcState) {
switch (earcState) {
case HDMI_EARC_STATUS_IDLE:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 1b527daafd24..0b667fc10880 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,6 +21,7 @@ import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -31,6 +32,7 @@ import android.os.PowerManager;
import android.os.SystemProperties;
import android.sysprop.HdmiProperties;
import android.util.Slog;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
@@ -82,6 +84,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// lost.
private Handler mDelayedPopupOnActiveSourceLostHandler;
+ private boolean mIsActiveSourceLostPopupLaunched;
+
// Determines what action should be taken upon receiving Routing Control messages.
@VisibleForTesting
protected HdmiProperties.playback_device_action_on_routing_control_values
@@ -96,6 +100,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
mDelayedStandbyOnActiveSourceLostHandler = new Handler(service.getServiceLooper());
mDelayedPopupOnActiveSourceLostHandler = new Handler(service.getServiceLooper());
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
+ mIsActiveSourceLostPopupLaunched = false;
}
@Override
@@ -275,6 +280,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
public void run() {
if (!isActiveSource()) {
mService.standby();
+ mIsActiveSourceLostPopupLaunched = false;
}
}
}
@@ -283,6 +289,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
void dismissUiOnActiveSourceStatusRecovered() {
assertRunOnServiceThread();
Intent intent = new Intent(HdmiControlManager.ACTION_ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI);
+ mIsActiveSourceLostPopupLaunched = false;
mService.sendBroadcastAsUser(intent);
}
@@ -516,6 +523,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
)));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivityAsUser(intent, context.getUser());
+ mIsActiveSourceLostPopupLaunched = true;
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Unable to start HdmiCecActiveSourceLostActivity");
} finally {
@@ -733,6 +741,14 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
return Constants.ADDR_TV;
}
+ boolean isActiveSourceLostPopupLaunched() {
+ return mIsActiveSourceLostPopupLaunched;
+ }
+
+ void setIsActiveSourceLostPopupLaunched(boolean isActiveSourceLostPopupLaunched) {
+ mIsActiveSourceLostPopupLaunched = isActiveSourceLostPopupLaunched;
+ }
+
@Override
@ServiceThreadOnly
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f049ef316cb1..35ef18b144e7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -50,6 +50,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayManager;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
@@ -106,6 +107,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1007,6 +1009,21 @@ public class HdmiControlService extends SystemService {
}
}, mServiceThreadExecutor);
+ if (isPlaybackDevice()) {
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean goToStandbyOnActiveSourceLost =
+ mHdmiCecConfig.getStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)
+ .equals(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+ writePowerStateChangeOnActiveSourceLostAtom(goToStandbyOnActiveSourceLost);
+ }
+ }, mServiceThreadExecutor);
+ }
+
mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -3189,6 +3206,7 @@ public class HdmiControlService extends SystemService {
// Cancel an existing timer to send the device to sleep since OTP was triggered.
playback().mDelayedStandbyOnActiveSourceLostHandler
.removeCallbacksAndMessages(null);
+ playback().setIsActiveSourceLostPopupLaunched(false);
}
if (source == null) {
@@ -5227,6 +5245,34 @@ public class HdmiControlService extends SystemService {
}
/**
+ * Writes a HdmiPowerStateChangeOnActiveSourceLostToggled atom representing a
+ * HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST setting change.
+ */
+ protected void writePowerStateChangeOnActiveSourceLostAtom(boolean isSettingEnabled) {
+ String manufacturerPnpId = "undefined";
+ int manufactureYear = -1;
+ int manufactureWeek = -1;
+ Display display = getContext().getDisplay();
+ if (display != null) {
+ DeviceProductInfo deviceProductInfo = display.getDeviceProductInfo();
+ manufacturerPnpId = deviceProductInfo.getManufacturerPnpId();
+ manufactureYear = deviceProductInfo.getManufactureYear();
+ }
+ int enumLogReason =
+ HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_UNKNOWN;
+ if (playback() != null) {
+ if (playback().isActiveSourceLostPopupLaunched()) {
+ enumLogReason = HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_POP_UP;
+ } else {
+ enumLogReason = HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_SETTING;
+ }
+ }
+
+ getAtomWriter().powerStateChangeOnActiveSourceLostChanged(isSettingEnabled, enumLogReason,
+ manufacturerPnpId, manufactureYear, manufactureWeek);
+ }
+
+ /**
* Reads the property that checks if CEC was enabled by the user while in offline mode such that
* it won't be disabled when going to sleep by low energy mode.
*/
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 2e167efc4d81..6053557f575c 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -108,6 +108,7 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.eventlog.LocationEventLog;
+import com.android.server.location.fudger.LocationFudgerCache;
import com.android.server.location.geofence.GeofenceManager;
import com.android.server.location.geofence.GeofenceProxy;
import com.android.server.location.gnss.GnssConfiguration;
@@ -147,6 +148,7 @@ import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
import com.android.server.location.provider.proxy.ProxyGeocodeProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
+import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;
import com.android.server.location.settings.LocationSettings;
import com.android.server.location.settings.LocationUserSettings;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
@@ -260,6 +262,11 @@ public class LocationManagerService extends ILocationManager.Stub implements
private volatile @Nullable GnssManagerService mGnssManagerService = null;
private ProxyGeocodeProvider mGeocodeProvider;
+ private @Nullable ProxyPopulationDensityProvider mPopulationDensityProvider = null;
+
+ // A cache for population density lookups. Used if density-based coarse locations are enabled.
+ private @Nullable LocationFudgerCache mLocationFudgerCache = null;
+
private final Object mDeprecatedGnssBatchingLock = new Object();
@GuardedBy("mDeprecatedGnssBatchingLock")
private @Nullable ILocationListener mDeprecatedGnssBatchingListener;
@@ -392,6 +399,25 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
}
+ @VisibleForTesting
+ protected void setProxyPopulationDensityProvider(ProxyPopulationDensityProvider provider) {
+ if (Flags.populationDensityProvider()) {
+ mPopulationDensityProvider = provider;
+ }
+ }
+
+ @VisibleForTesting
+ protected void setLocationFudgerCache(LocationFudgerCache cache) {
+ if (!Flags.densityBasedCoarseLocations()) {
+ return;
+ }
+
+ mLocationFudgerCache = cache;
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.setLocationFudgerCache(cache);
+ }
+ }
+
private void removeLocationProviderManager(LocationProviderManager manager) {
synchronized (mProviderManagers) {
boolean removed = mProviderManagers.remove(manager);
@@ -510,6 +536,17 @@ public class LocationManagerService extends ILocationManager.Stub implements
Log.e(TAG, "no geocoder provider found");
}
+ if (Flags.populationDensityProvider()) {
+ setProxyPopulationDensityProvider(
+ ProxyPopulationDensityProvider.createAndRegister(mContext));
+ if (mPopulationDensityProvider == null) {
+ Log.e(TAG, "no population density provider found");
+ }
+ }
+ if (mPopulationDensityProvider != null && Flags.densityBasedCoarseLocations()) {
+ setLocationFudgerCache(new LocationFudgerCache(mPopulationDensityProvider));
+ }
+
// bind to hardware activity recognition
HardwareActivityRecognitionProxy hardwareActivityRecognitionProxy =
HardwareActivityRecognitionProxy.createAndRegister(mContext);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
new file mode 100644
index 000000000000..53389cafecef
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.content.Context;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
+import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.IContextHubEndpoint;
+import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.hardware.location.IContextHubTransactionCallback;
+
+/**
+ * A class that represents a broker for the endpoint registered by the client app. This class
+ * manages direct IContextHubEndpoint/IContextHubEndpointCallback API/callback calls.
+ *
+ * @hide
+ */
+public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
+ private static final String TAG = "ContextHubEndpointBroker";
+
+ /** The context of the service. */
+ private final Context mContext;
+
+ /** The proxy to talk to the Context Hub HAL. */
+ private final IContextHubWrapper mContextHubProxy;
+
+ /** The manager that registered this endpoint. */
+ private final ContextHubEndpointManager mEndpointManager;
+
+ /** Metadata about this endpoint (app-facing container). */
+ private final HubEndpointInfo mEndpointInfo;
+
+ /** Metadata about this endpoint (HAL-facing container). */
+ private final EndpointInfo mHalEndpointInfo;
+
+ /** The remote callback interface for this endpoint. */
+ private final IContextHubEndpointCallback mContextHubEndpointCallback;
+
+ /* package */ ContextHubEndpointBroker(
+ Context context,
+ IContextHubWrapper contextHubProxy,
+ ContextHubEndpointManager endpointManager,
+ EndpointInfo halEndpointInfo,
+ IContextHubEndpointCallback callback) {
+ mContext = context;
+ mContextHubProxy = contextHubProxy;
+ mEndpointManager = endpointManager;
+ mEndpointInfo = new HubEndpointInfo(halEndpointInfo);
+ mHalEndpointInfo = halEndpointInfo;
+ mContextHubEndpointCallback = callback;
+ }
+
+ @Override
+ public HubEndpointInfo getAssignedHubEndpointInfo() {
+ return mEndpointInfo;
+ }
+
+ @Override
+ public int openSession(HubEndpointInfo destination, HubServiceInfo serviceInfo) {
+ // TODO(b/378487799): Implement this
+ return 0;
+ }
+
+ @Override
+ public void closeSession(int sessionId, int reason) {
+ // TODO(b/378487799): Implement this
+
+ }
+
+ @Override
+ public void openSessionRequestComplete(int sessionId) {
+ // TODO(b/378487799): Implement this
+
+ }
+
+ @Override
+ public void unregister() {
+ // TODO(b/378487799): Implement this
+
+ }
+
+ @Override
+ public void sendMessage(
+ int sessionId, HubMessage message, IContextHubTransactionCallback callback) {
+ // TODO(b/381102453): Implement this
+ }
+
+ @Override
+ public void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode) {
+ // TODO(b/381102453): Implement this
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
new file mode 100644
index 000000000000..54ce74f7b2f1
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.content.Context;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.IContextHubEndpoint;
+import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A class that manages registration/unregistration of clients and manages messages to/from clients.
+ *
+ * @hide
+ */
+/* package */ class ContextHubEndpointManager {
+ private static final String TAG = "ContextHubEndpointManager";
+
+ /** The hub ID of the Context Hub Service. */
+ private static final long SERVICE_HUB_ID = 0x416e64726f696400L;
+
+ /** The context of the service. */
+ private final Context mContext;
+
+ /** The proxy to talk to the Context Hub. */
+ private final IContextHubWrapper mContextHubProxy;
+
+ /** A map of endpoint IDs to brokers currently registered. */
+ private final Map<Long, ContextHubEndpointBroker> mEndpointMap = new ConcurrentHashMap<>();
+
+ /** Variables for managing endpoint ID creation */
+ private final Object mEndpointLock = new Object();
+
+ @GuardedBy("mEndpointLock")
+ private long mNextEndpointId = 0;
+
+ /* package */ ContextHubEndpointManager(Context context, IContextHubWrapper contextHubProxy) {
+ mContext = context;
+ mContextHubProxy = contextHubProxy;
+ }
+
+ /**
+ * Registers a new endpoint with the service.
+ *
+ * @param pendingEndpointInfo the object describing the endpoint being registered
+ * @param callback the callback interface of the endpoint to register
+ * @return the endpoint interface
+ * @throws IllegalStateException if max number of endpoints have already registered
+ */
+ /* package */ IContextHubEndpoint registerEndpoint(
+ HubEndpointInfo pendingEndpointInfo, IContextHubEndpointCallback callback)
+ throws RemoteException {
+ ContextHubEndpointBroker broker;
+ long endpointId = getNewEndpointId();
+ EndpointInfo halEndpointInfo =
+ ContextHubServiceUtil.createHalEndpointInfo(
+ pendingEndpointInfo, endpointId, SERVICE_HUB_ID);
+ try {
+ mContextHubProxy.registerEndpoint(halEndpointInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling HAL registerEndpoint", e);
+ throw e;
+ }
+ broker =
+ new ContextHubEndpointBroker(
+ mContext,
+ mContextHubProxy,
+ this /* endpointManager */,
+ halEndpointInfo,
+ callback);
+ mEndpointMap.put(endpointId, broker);
+
+ // TODO(b/378487799): Add death recipient
+
+ Log.d(TAG, "Registered endpoint with ID = " + endpointId);
+ return IContextHubEndpoint.Stub.asInterface(broker);
+ }
+
+ /**
+ * @return an available endpoint ID
+ */
+ private long getNewEndpointId() {
+ synchronized (mEndpointLock) {
+ if (mNextEndpointId == Long.MAX_VALUE) {
+ throw new IllegalStateException("Too many endpoints connected");
+ }
+ return mNextEndpointId++;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index d177d0e5f2eb..0d926b99217d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -155,6 +155,9 @@ public class ContextHubService extends IContextHubService.Stub {
// The manager for sending messages to/from clients
private ContextHubClientManager mClientManager;
+ // Manages endpoint and its sessions between apps and HAL
+ private ContextHubEndpointManager mEndpointManager;
+
// The default client for old API clients
private Map<Integer, IContextHubClient> mDefaultClientMap;
@@ -330,14 +333,17 @@ public class ContextHubService extends IContextHubService.Stub {
HubInfoRegistry registry;
try {
registry = new HubInfoRegistry(mContextHubWrapper);
+ mEndpointManager = new ContextHubEndpointManager(mContext, mContextHubWrapper);
Log.i(TAG, "Enabling generic offload API");
} catch (UnsupportedOperationException e) {
+ mEndpointManager = null;
registry = null;
Log.w(TAG, "Generic offload API not supported, disabling");
}
mHubInfoRegistry = registry;
} else {
mHubInfoRegistry = null;
+ mEndpointManager = null;
Log.i(TAG, "Disabling generic offload API");
}
@@ -790,8 +796,11 @@ public class ContextHubService extends IContextHubService.Stub {
HubEndpointInfo pendingHubEndpointInfo, IContextHubEndpointCallback callback)
throws RemoteException {
super.registerEndpoint_enforcePermission();
- // TODO(b/375487784): Implement this
- return null;
+ if (mEndpointManager == null) {
+ Log.e(TAG, "ContextHubService.registerEndpoint: endpoint manager failed to initialize");
+ throw new UnsupportedOperationException("Endpoint registration is not supported");
+ }
+ return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 33d2ff02986a..05be427f9daf 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -18,6 +18,9 @@ package com.android.server.location.contexthub;
import android.Manifest;
import android.content.Context;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.HostEndPoint;
@@ -415,4 +418,51 @@ import java.util.List;
static String formatDateFromTimestamp(long timeStampInMs) {
return DATE_FORMATTER.format(Instant.ofEpochMilli(timeStampInMs));
}
+
+ /**
+ * Converts a context hub HAL EndpointInfo object based on the provided HubEndpointInfo.
+ *
+ * @param info the HubEndpointInfo object
+ * @return the equivalent EndpointInfo object
+ */
+ /* package */
+ static EndpointInfo convertHalEndpointInfo(HubEndpointInfo info) {
+ return createHalEndpointInfo(
+ info, info.getIdentifier().getEndpoint(), info.getIdentifier().getHub());
+ }
+
+ /**
+ * Creates a context hub HAL EndpointInfo object based on the provided HubEndpointInfo. As
+ * opposed to convertHalEndpointInfo, this method can be used to overwrite/specify the endpoint
+ * and hub ID.
+ *
+ * @param info the HubEndpointInfo object
+ * @param endpointId the endpoint ID of this object
+ * @param hubId the hub ID of this object
+ * @return the equivalent EndpointInfo object
+ */
+ /* package */
+ static EndpointInfo createHalEndpointInfo(HubEndpointInfo info, long endpointId, long hubId) {
+ EndpointInfo outputInfo = new EndpointInfo();
+ outputInfo.id = new android.hardware.contexthub.EndpointId();
+ outputInfo.id.id = endpointId;
+ outputInfo.id.hubId = hubId;
+ outputInfo.name = info.getName();
+ outputInfo.version = info.getVersion();
+ outputInfo.tag = info.getTag();
+ Collection<String> permissions = info.getRequiredPermissions();
+ outputInfo.requiredPermissions = permissions.toArray(new String[permissions.size()]);
+ Collection<HubServiceInfo> services = info.getServiceInfoCollection();
+ outputInfo.services = new android.hardware.contexthub.Service[services.size()];
+ int i = 0;
+ for (HubServiceInfo service : services) {
+ outputInfo.services[i] = new android.hardware.contexthub.Service();
+ outputInfo.services[i].format = service.getFormat();
+ outputInfo.services[i].serviceDescriptor = service.getServiceDescriptor();
+ outputInfo.services[i].majorVersion = service.getMajorVersion();
+ outputInfo.services[i].minorVersion = service.getMinorVersion();
+ i++;
+ }
+ return outputInfo;
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 9b729eb11eed..14d75b02d6b0 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -239,6 +239,10 @@ public abstract class IContextHubWrapper {
public void registerEndpointCallback(android.hardware.contexthub.IEndpointCallback cb)
throws RemoteException {}
+ /** Registers the endpoint with the ContextHub HAL */
+ public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {}
+
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
*/
@@ -671,6 +675,16 @@ public abstract class IContextHubWrapper {
hub.registerEndpointCallback(cb);
}
+ @Override
+ public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.registerEndpoint(info);
+ }
+
public boolean supportsLocationSettingNotifications() {
return true;
}
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index 88a269706470..0da1514872d6 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -16,13 +16,16 @@
package com.android.server.location.fudger;
+import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
+import android.location.flags.Flags;
import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.geometry.S2CellIdUtils;
import java.security.SecureRandom;
import java.time.Clock;
@@ -83,6 +86,9 @@ public class LocationFudger {
@GuardedBy("this")
@Nullable private LocationResult mCachedCoarseLocationResult;
+ @GuardedBy("this")
+ @Nullable private LocationFudgerCache mLocationFudgerCache = null;
+
public LocationFudger(float accuracyM) {
this(accuracyM, SystemClock.elapsedRealtimeClock(), new SecureRandom());
}
@@ -97,6 +103,16 @@ public class LocationFudger {
}
/**
+ * Provides the optional {@link LocationFudgerCache} for coarsening based on population density.
+ */
+ @FlaggedApi(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS)
+ public void setLocationFudgerCache(LocationFudgerCache cache) {
+ synchronized (this) {
+ mLocationFudgerCache = cache;
+ }
+ }
+
+ /**
* Resets the random offsets completely.
*/
public void resetOffsets() {
@@ -162,16 +178,34 @@ public class LocationFudger {
longitude += wrapLongitude(metersToDegreesLongitude(mLongitudeOffsetM, latitude));
latitude += wrapLatitude(metersToDegreesLatitude(mLatitudeOffsetM));
- // quantize location by snapping to a grid. this is the primary means of obfuscation. it
- // gives nice consistent results and is very effective at hiding the true location (as long
- // as you are not sitting on a grid boundary, which the random offsets mitigate).
- //
- // note that we quantize the latitude first, since the longitude quantization depends on the
- // latitude value and so leaks information about the latitude
- double latGranularity = metersToDegreesLatitude(mAccuracyM);
- latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
- double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
- longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
+ // We copy a reference to the cache, so even if mLocationFudgerCache is concurrently set
+ // to null, we can continue executing the condition below.
+ LocationFudgerCache cacheCopy = null;
+ synchronized (this) {
+ cacheCopy = mLocationFudgerCache;
+ }
+
+ // TODO(b/381204398): To ensure a safe rollout, two algorithms co-exist. The first is the
+ // new density-based algorithm, while the second is the traditional coarsening algorithm.
+ // Once rollout is done, clean up the unused algorithm.
+ if (Flags.densityBasedCoarseLocations() && cacheCopy != null
+ && cacheCopy.hasDefaultValue()) {
+ int level = cacheCopy.getCoarseningLevel(latitude, longitude);
+ double[] center = snapToCenterOfS2Cell(latitude, longitude, level);
+ latitude = center[S2CellIdUtils.LAT_INDEX];
+ longitude = center[S2CellIdUtils.LNG_INDEX];
+ } else {
+ // quantize location by snapping to a grid. this is the primary means of obfuscation. it
+ // gives nice consistent results and is very effective at hiding the true location (as
+ // long as you are not sitting on a grid boundary, which the random offsets mitigate).
+ //
+ // note that we quantize the latitude first, since the longitude quantization depends on
+ // the latitude value and so leaks information about the latitude
+ double latGranularity = metersToDegreesLatitude(mAccuracyM);
+ latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity);
+ double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude);
+ longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity);
+ }
coarse.setLatitude(latitude);
coarse.setLongitude(longitude);
@@ -185,6 +219,15 @@ public class LocationFudger {
return coarse;
}
+ @VisibleForTesting
+ protected double[] snapToCenterOfS2Cell(double latDegrees, double lngDegrees, int level) {
+ long leafCell = S2CellIdUtils.fromLatLngDegrees(latDegrees, lngDegrees);
+ long coarsenedCell = S2CellIdUtils.getParent(leafCell, level);
+ double[] center = new double[] {0.0, 0.0};
+ S2CellIdUtils.toLatLngDegrees(coarsenedCell, center);
+ return center;
+ }
+
/**
* Update the random offsets over time.
*
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
new file mode 100644
index 000000000000..3670c1f5c51b
--- /dev/null
+++ b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.fudger;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.flags.Flags;
+import android.location.provider.IS2CellIdsCallback;
+import android.location.provider.IS2LevelCallback;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.geometry.S2CellIdUtils;
+import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;
+
+import java.util.Objects;
+
+/**
+ * A cache for returning the coarsening level to be used. The coarsening level depends on the user
+ * location. If the cache contains the requested latitude/longitude, the s2 level of the cached
+ * cell id is returned. If not, a default value is returned.
+ * This class has a {@link ProxyPopulationDensityProvider} used to refresh the cache.
+ * This cache exists because {@link ProxyPopulationDensityProvider} must be queried asynchronously,
+ * whereas a synchronous answer is needed.
+ * The cache is first-in, first-out, and has a fixed size. Cache entries are valid until evicted by
+ * another value.
+ */
+@FlaggedApi(Flags.FLAG_POPULATION_DENSITY_PROVIDER)
+public class LocationFudgerCache {
+
+ // The maximum number of S2 cell ids stored in the cache.
+ // Each cell id is a long, so the memory requirement is 8*MAX_CACHE_SIZE bytes.
+ protected static final int MAX_CACHE_SIZE = 20;
+
+ private final Object mLock = new Object();
+
+ // mCache is a circular buffer of size MAX_CACHE_SIZE. The next position to be written to is
+ // mPosInCache. Initially, the cache is filled with INVALID_CELL_IDs.
+ @GuardedBy("mLock")
+ private final long[] mCache = new long[MAX_CACHE_SIZE];
+
+ @GuardedBy("mLock")
+ private int mPosInCache = 0;
+
+ @GuardedBy("mLock")
+ private int mCacheSize = 0;
+
+ // The S2 level to coarsen to, if the cache doesn't contain a better answer.
+ // Updated concurrently by callbacks.
+ @GuardedBy("mLock")
+ private Integer mDefaultCoarseningLevel = null;
+
+ // The provider that asynchronously provides what is stored in the cache.
+ private final ProxyPopulationDensityProvider mPopulationDensityProvider;
+
+ private static String sTAG = "LocationFudgerCache";
+
+ public LocationFudgerCache(@NonNull ProxyPopulationDensityProvider provider) {
+ mPopulationDensityProvider = Objects.requireNonNull(provider);
+
+ asyncFetchDefaultCoarseningLevel();
+ }
+
+ /** Returns true if the cache has successfully received a default value from the provider. */
+ public boolean hasDefaultValue() {
+ synchronized (mLock) {
+ return (mDefaultCoarseningLevel != null);
+ }
+ }
+
+ /**
+ * Returns the S2 level to which the provided location should be coarsened.
+ * The answer comes from the cache if available, otherwise the default value is returned.
+ */
+ public int getCoarseningLevel(double latitudeDegrees, double longitudeDegrees) {
+ // If we still haven't received the default level from the provider, try fetching it again.
+ // The answer wouldn't come in time, but it will be used for the following queries.
+ if (!hasDefaultValue()) {
+ asyncFetchDefaultCoarseningLevel();
+ }
+ Long s2CellId = readCacheForLatLng(latitudeDegrees, longitudeDegrees);
+ if (s2CellId == null) {
+ // Asynchronously queries the density from the provider. The answer won't come in time,
+ // but it will update the cache for the following queries.
+ refreshCache(latitudeDegrees, longitudeDegrees);
+
+ return getDefaultCoarseningLevel();
+ }
+ return S2CellIdUtils.getLevel(s2CellId);
+ }
+
+ /**
+ * If the cache contains the current location, returns the corresponding S2 cell id.
+ * Otherwise, returns null.
+ */
+ @Nullable
+ private Long readCacheForLatLng(double latDegrees, double lngDegrees) {
+ synchronized (mLock) {
+ for (int i = 0; i < mCacheSize; i++) {
+ if (S2CellIdUtils.containsLatLngDegrees(mCache[i], latDegrees, lngDegrees)) {
+ return mCache[i];
+ }
+ }
+ }
+ return null;
+ }
+
+ /** Adds the provided s2 cell id to the cache. This might evict other values from the cache. */
+ public void addToCache(long s2CellId) {
+ addToCache(new long[] {s2CellId});
+ }
+
+ /**
+ * Adds the provided s2 cell ids to the cache. This might evict other values from the cache.
+ * If more than MAX_CACHE_SIZE elements are provided, only the first elements are copied.
+ * The first element of the input is added last into the FIFO cache, so it gets evicted last.
+ */
+ public void addToCache(long[] s2CellIds) {
+ synchronized (mLock) {
+ // Only copy up to MAX_CACHE_SIZE elements
+ int end = Math.min(s2CellIds.length, MAX_CACHE_SIZE);
+ mCacheSize = Math.min(mCacheSize + end, MAX_CACHE_SIZE);
+
+ // Add in reverse so the first cell of s2CellIds is the last evicted
+ for (int i = end - 1; i >= 0; i--) {
+ mCache[mPosInCache] = s2CellIds[i];
+ mPosInCache = (mPosInCache + 1) % MAX_CACHE_SIZE;
+ }
+ }
+ }
+
+ /**
+ * Queries the population density provider for the default coarsening level (to be used if the
+ * cache doesn't contain a better answer), and updates mDefaultCoarseningLevel with the answer.
+ */
+ private void asyncFetchDefaultCoarseningLevel() {
+ IS2LevelCallback callback = new IS2LevelCallback.Stub() {
+ @Override
+ public void onResult(int s2level) {
+ synchronized (mLock) {
+ mDefaultCoarseningLevel = Integer.valueOf(s2level);
+ }
+ }
+
+ @Override
+ public void onError() {
+ Log.e(sTAG, "could not get default population density");
+ }
+ };
+ mPopulationDensityProvider.getDefaultCoarseningLevel(callback);
+ }
+
+ /**
+ * Queries the population density provider and store the result in the cache.
+ */
+ private void refreshCache(double latitude, double longitude) {
+ IS2CellIdsCallback callback = new IS2CellIdsCallback.Stub() {
+ @Override
+ public void onResult(long[] s2CellIds) {
+ addToCache(s2CellIds);
+ }
+
+ @Override
+ public void onError() {
+ Log.e(sTAG, "could not get population density");
+ }
+ };
+ mPopulationDensityProvider.getCoarsenedS2Cell(latitude, longitude, callback);
+ }
+
+ /**
+ * Returns the default S2 level to coarsen to. This should be used if the cache
+ * does not provide a better answer.
+ */
+ private int getDefaultCoarseningLevel() {
+ synchronized (mLock) {
+ // The minimum valid level is 0.
+ if (mDefaultCoarseningLevel == null) {
+ return 0;
+ }
+ return mDefaultCoarseningLevel;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 4a9bf88aae33..a8c90100ebf0 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -48,6 +48,7 @@ import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import static java.lang.Math.max;
import static java.lang.Math.min;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -105,6 +106,7 @@ import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
import com.android.server.location.fudger.LocationFudger;
+import com.android.server.location.fudger.LocationFudgerCache;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.AppForegroundHelper.AppForegroundListener;
@@ -1663,6 +1665,18 @@ public class LocationProviderManager extends
}
/**
+ * Provides the optional {@link LocationFudgerCache} for coarsening based on population density.
+ */
+ @FlaggedApi(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS)
+ public void setLocationFudgerCache(LocationFudgerCache cache) {
+ if (!Flags.densityBasedCoarseLocations()) {
+ return;
+ }
+
+ mLocationFudger.setLocationFudgerCache(cache);
+ }
+
+ /**
* Returns true if this provider is visible to the current caller (whether called from a binder
* thread or not). If a provider isn't visible, then all APIs return the same data they would if
* the provider didn't exist (i.e. the caller can't see or use the provider).
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java
new file mode 100644
index 000000000000..b0a0f0b0c83b
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.provider.proxy;
+
+import static android.location.provider.PopulationDensityProviderBase.ACTION_POPULATION_DENSITY_PROVIDER;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.provider.IPopulationDensityProvider;
+import android.location.provider.IS2CellIdsCallback;
+import android.location.provider.IS2LevelCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.servicewatcher.CurrentUserServiceSupplier;
+import com.android.server.servicewatcher.ServiceWatcher;
+
+/**
+ * Proxy for IPopulationDensityProvider implementations.
+ */
+public class ProxyPopulationDensityProvider {
+
+ public static final String TAG = "ProxyPopulationDensityProvider";
+
+ final ServiceWatcher mServiceWatcher;
+
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static ProxyPopulationDensityProvider createAndRegister(Context context) {
+ ProxyPopulationDensityProvider proxy = new ProxyPopulationDensityProvider(context);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private ProxyPopulationDensityProvider(Context context) {
+ mServiceWatcher = ServiceWatcher.create(
+ context,
+ "PopulationDensityProxy",
+ CurrentUserServiceSupplier.createFromConfig(
+ context,
+ ACTION_POPULATION_DENSITY_PROVIDER,
+ com.android.internal.R.bool.config_enablePopulationDensityProviderOverlay,
+ com.android.internal.R.string.config_populationDensityProviderPackageName),
+ null);
+ }
+
+ private boolean register() {
+ boolean resolves = mServiceWatcher.checkServiceResolves();
+ if (resolves) {
+ mServiceWatcher.register();
+ }
+ return resolves;
+ }
+
+ /** Gets the default coarsening level. */
+ public void getDefaultCoarseningLevel(IS2LevelCallback callback) {
+ mServiceWatcher.runOnBinder(
+ new ServiceWatcher.BinderOperation() {
+ @Override
+ public void run(IBinder binder) throws RemoteException {
+ IPopulationDensityProvider.Stub.asInterface(binder)
+ .getDefaultCoarseningLevel(callback);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "remote exception while querying default coarsening level");
+ }
+ }
+ });
+ }
+
+
+ /** Gets the population density at the requested location. */
+ public void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
+ IS2CellIdsCallback callback) {
+ mServiceWatcher.runOnBinder(
+ new ServiceWatcher.BinderOperation() {
+ @Override
+ public void run(IBinder binder) throws RemoteException {
+ IPopulationDensityProvider.Stub.asInterface(binder)
+ .getCoarsenedS2Cell(latitudeDegrees, longitudeDegrees, callback);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "remote exception while querying coarsened S2 cell");
+ }
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 2d2d2584edfd..ab68ed3e73c6 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -221,11 +221,6 @@ class MediaRouter2ServiceImpl {
}
}
- @NonNull
- private SystemMediaRoute2Provider getSystemProviderForUser(@NonNull UserHandler userHandler) {
- return userHandler.mSystemProvider;
- }
-
// Start of methods that implement MediaRouter2 operations.
@NonNull
@@ -251,7 +246,7 @@ class MediaRouter2ServiceImpl {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
if (hasSystemRoutingPermissions) {
MediaRoute2ProviderInfo providerInfo =
- getSystemProviderForUser(userRecord.mHandler).getProviderInfo();
+ userRecord.mHandler.getSystemProvider().getProviderInfo();
if (providerInfo != null) {
systemRoutes = providerInfo.getRoutes();
} else {
@@ -264,7 +259,7 @@ class MediaRouter2ServiceImpl {
} else {
systemRoutes = new ArrayList<>();
systemRoutes.add(
- getSystemProviderForUser(userRecord.mHandler).getDefaultRoute());
+ userRecord.mHandler.getSystemProvider().getDefaultRoute());
}
}
return new ArrayList<>(systemRoutes);
@@ -856,11 +851,10 @@ class MediaRouter2ServiceImpl {
if (setDeviceRouteSelected) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
- return getSystemProviderForUser(userRecord.mHandler)
+ return userRecord.mHandler.getSystemProvider()
.generateDeviceRouteSelectedSessionInfo(targetPackageName);
} else {
- sessionInfos = getSystemProviderForUser(userRecord.mHandler)
- .getSessionInfos();
+ sessionInfos = userRecord.mHandler.getSystemProvider().getSessionInfos();
if (!sessionInfos.isEmpty()) {
// Return a copy of the current system session with no modification,
// except setting the client package name.
@@ -873,8 +867,7 @@ class MediaRouter2ServiceImpl {
}
} else {
return new RoutingSessionInfo.Builder(
- getSystemProviderForUser(userRecord.mHandler)
- .getDefaultSessionInfo())
+ userRecord.mHandler.getSystemProvider().getDefaultSessionInfo())
.setClientPackageName(targetPackageName)
.build();
}
@@ -1144,6 +1137,7 @@ class MediaRouter2ServiceImpl {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
RouterRecord routerRecord =
new RouterRecord(
+ mContext,
userRecord,
router,
uid,
@@ -1382,7 +1376,7 @@ class MediaRouter2ServiceImpl {
}
manager.mLastSessionCreationRequest = null;
} else {
- String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
+ String defaultRouteId = userHandler.getSystemProvider().getDefaultRoute().getId();
if (route.isSystemRoute()
&& !routerRecord.hasSystemRoutingPermission()
&& !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -1470,7 +1464,7 @@ class MediaRouter2ServiceImpl {
routerRecord.mPackageName, routerRecord.mRouterId, route.getId()));
UserHandler userHandler = routerRecord.mUserRecord.mHandler;
- String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
+ String defaultRouteId = userHandler.getSystemProvider().getDefaultRoute().getId();
if (route.isSystemRoute()
&& !routerRecord.hasSystemRoutingPermission()
&& !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -2063,6 +2057,7 @@ class MediaRouter2ServiceImpl {
}
final class RouterRecord implements IBinder.DeathRecipient {
+ public final Context mContext;
public final UserRecord mUserRecord;
public final String mPackageName;
public final List<Integer> mSelectRouteSequenceNumbers;
@@ -2081,6 +2076,7 @@ class MediaRouter2ServiceImpl {
@Nullable public RouteListingPreference mRouteListingPreference;
RouterRecord(
+ Context context,
UserRecord userRecord,
IMediaRouter2 router,
int uid,
@@ -2090,6 +2086,7 @@ class MediaRouter2ServiceImpl {
boolean hasModifyAudioRoutingPermission,
boolean hasMediaContentControlPermission,
boolean hasMediaRoutingControl) {
+ mContext = context;
mUserRecord = userRecord;
mPackageName = packageName;
mSelectRouteSequenceNumbers = new ArrayList<>();
@@ -2133,12 +2130,11 @@ class MediaRouter2ServiceImpl {
notifyRoutesUpdated(routesToReport.values().stream().toList());
List<RoutingSessionInfo> sessionInfos =
- getSystemProviderForUser(mUserRecord.mHandler).getSessionInfos();
+ mUserRecord.mHandler.getSystemProvider().getSessionInfos();
RoutingSessionInfo systemSessionToReport =
newSystemRoutingPermissionValue && !sessionInfos.isEmpty()
? sessionInfos.get(0)
- : getSystemProviderForUser(mUserRecord.mHandler)
- .getDefaultSessionInfo();
+ : mUserRecord.mHandler.getSystemProvider().getDefaultSessionInfo();
notifySessionInfoChanged(systemSessionToReport);
}
}
@@ -2288,7 +2284,7 @@ class MediaRouter2ServiceImpl {
if (route.isSystemRoute() && !hasSystemRoutingPermission()) {
// The router lacks permission to modify system routing, so we hide system
// route info from them.
- route = getSystemProviderForUser(mUserRecord.mHandler).getDefaultRoute();
+ route = mUserRecord.mHandler.getSystemProvider().getDefaultRoute();
}
mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route);
} catch (RemoteException ex) {
@@ -2331,18 +2327,34 @@ class MediaRouter2ServiceImpl {
}
/**
- * Returns a filtered copy of {@code routes} that contains only the routes that are {@link
- * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
+ * Returns a filtered copy of {@code routes} that contains only the routes that are visible
+ * to this RouterRecord.
*/
private List<MediaRoute2Info> getVisibleRoutes(@NonNull List<MediaRoute2Info> routes) {
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : routes) {
- if (route.isVisibleTo(mPackageName)) {
+ if (route.isVisibleTo(mPackageName) && hasPermissionsToSeeRoute(route)) {
filteredRoutes.add(route);
}
}
return filteredRoutes;
}
+
+ /**
+ * @return whether this RouterRecord has the required permissions to see the given route.
+ */
+ private boolean hasPermissionsToSeeRoute(MediaRoute2Info route) {
+ if (!Flags.enableRouteVisibilityControlApi()) {
+ return true;
+ }
+ for (String permission : route.getRequiredPermissions()) {
+ if (mContext.checkPermission(permission, mPid, mUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
}
final class ManagerRecord implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index ceb931400d48..516b002885af 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -2329,7 +2329,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
- mInstallDependencyHelper.notifySessionComplete(session.sessionId, success);
+ if (Flags.sdkDependencyInstaller()) {
+ mInstallDependencyHelper.notifySessionComplete(
+ session.sessionId, success);
+ }
final File appIconFile = buildAppIconFile(session.sessionId);
if (appIconFile.exists()) {
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index e49dc8250bc7..976999cf6ae0 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -426,6 +426,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_PREBUILT = 23;
private static final int TRON_COMPILATION_REASON_VDEX = 24;
private static final int TRON_COMPILATION_REASON_BOOT_AFTER_MAINLINE_UPDATE = 25;
+ private static final int TRON_COMPILATION_REASON_CLOUD = 26;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
@@ -460,6 +461,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
return TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
case "install-bulk-secondary-downgraded" :
return TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
+ case "cloud":
+ return TRON_COMPILATION_REASON_CLOUD;
// These are special markers for dex metadata installation that do not
// have an equivalent as a system property.
case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index aba15c83f907..0f6688f73c22 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -23,6 +23,7 @@ import static com.android.server.power.hint.Flags.adpfSessionTag;
import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import static com.android.server.power.hint.Flags.resetOnForkEnabled;
+import android.adpf.ISessionManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -193,6 +194,7 @@ public final class HintManagerService extends SystemService {
private final Object mCpuHeadroomLock = new Object();
+ private ISessionManager mSessionManager;
// this cache tracks the expiration time of the items and performs cleanup on lookup
private static class HeadroomCache<K, V> {
@@ -818,6 +820,23 @@ public final class HintManagerService extends SystemService {
for (int i = tokenMap.size() - 1; i >= 0; i--) {
// Will remove the session from tokenMap
ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(i);
+ IntArray closedSessionsForSf = new IntArray();
+ // Batch the closure call to SF for all the sessions that die
+ for (int j = sessionSet.size() - 1; j >= 0; j--) {
+ AppHintSession session = sessionSet.valueAt(j);
+ if (session.isTrackedBySf()) {
+ // Mark it as untracked so we don't untrack again on close
+ session.setTrackedBySf(false);
+ closedSessionsForSf.add(session.getSessionId());
+ }
+ }
+ if (mSessionManager != null) {
+ try {
+ mSessionManager.trackedSessionsDied(closedSessionsForSf.toArray());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to communicate with SessionManager");
+ }
+ }
for (int j = sessionSet.size() - 1; j >= 0; j--) {
sessionSet.valueAt(j).close();
}
@@ -1350,9 +1369,9 @@ public final class HintManagerService extends SystemService {
}
}
- final long sessionId = config.id != -1 ? config.id : halSessionPtr;
+ final long sessionIdForTracing = config.id != -1 ? config.id : halSessionPtr;
logPerformanceHintSessionAtom(
- callingUid, sessionId, durationNanos, tids, tag);
+ callingUid, sessionIdForTracing, durationNanos, tids, tag);
synchronized (mSessionSnapshotMapLock) {
// Update session snapshot upon session creation
@@ -1362,8 +1381,12 @@ public final class HintManagerService extends SystemService {
}
AppHintSession hs = null;
synchronized (mLock) {
+ Integer configId = null;
+ if (config.id != -1) {
+ configId = new Integer((int) config.id);
+ }
hs = new AppHintSession(callingUid, callingTgid, tag, tids,
- token, halSessionPtr, durationNanos);
+ token, halSessionPtr, durationNanos, configId);
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
mActiveSessions.get(callingUid);
if (tokenMap == null) {
@@ -1390,6 +1413,11 @@ public final class HintManagerService extends SystemService {
}
}
+ if (creationConfig.layerTokens != null
+ && creationConfig.layerTokens.length > 0) {
+ hs.associateToLayers(creationConfig.layerTokens);
+ }
+
synchronized (mThreadsUsageObject) {
mThreadsUsageMap.computeIfAbsent(callingUid, k -> new ArraySet<>());
ArraySet<ThreadUsageTracker> threadsSet = mThreadsUsageMap.get(callingUid);
@@ -1566,6 +1594,15 @@ public final class HintManagerService extends SystemService {
}
@Override
+ public void passSessionManagerBinder(IBinder sessionManager) {
+ // Ensure caller is internal
+ if (Process.myUid() != Binder.getCallingUid()) {
+ return;
+ }
+ mSessionManager = ISessionManager.Stub.asInterface(sessionManager);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
@@ -1688,6 +1725,8 @@ public final class HintManagerService extends SystemService {
protected boolean mHasBeenPowerEfficient;
protected boolean mHasBeenGraphicsPipeline;
protected boolean mShouldForcePause;
+ protected Integer mSessionId;
+ protected boolean mTrackedBySF;
enum SessionModes {
POWER_EFFICIENCY,
@@ -1696,7 +1735,7 @@ public final class HintManagerService extends SystemService {
protected AppHintSession(
int uid, int pid, int sessionTag, int[] threadIds, IBinder token,
- long halSessionPtr, long durationNanos) {
+ long halSessionPtr, long durationNanos, Integer sessionId) {
mUid = uid;
mPid = pid;
mTag = sessionTag;
@@ -1710,6 +1749,8 @@ public final class HintManagerService extends SystemService {
mHasBeenPowerEfficient = false;
mHasBeenGraphicsPipeline = false;
mShouldForcePause = false;
+ mSessionId = sessionId;
+ mTrackedBySF = false;
final boolean allowed = mUidObserver.isUidForeground(mUid);
updateHintAllowedByProcState(allowed);
try {
@@ -1799,6 +1840,19 @@ public final class HintManagerService extends SystemService {
} catch (NoSuchElementException ignored) {
Slogf.d(TAG, "Death link does not exist for session with UID " + mUid);
}
+ if (mTrackedBySF) {
+ if (mSessionManager != null) {
+ try {
+ mSessionManager.trackedSessionsDied(new int[]{mSessionId});
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Could not communicate with SessionManager", e);
+ }
+ mTrackedBySF = false;
+ } else {
+ Slog.e(TAG, "SessionManager is null but there are tracked sessions");
+ }
+ }
}
synchronized (mLock) {
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
@@ -1875,6 +1929,24 @@ public final class HintManagerService extends SystemService {
}
}
+ @Override
+ public void associateToLayers(IBinder[] layerTokens) {
+ synchronized (this) {
+ if (mSessionManager != null && mSessionId != null && layerTokens != null) {
+ // Sf only untracks a session when it dies
+ if (layerTokens.length > 0) {
+ mTrackedBySF = true;
+ }
+ try {
+ mSessionManager.associateSessionToLayers(mSessionId, mUid, layerTokens);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Could not communicate with SessionManager", e);
+ }
+ }
+ }
+ }
+
public void setThreads(@NonNull int[] tids) {
setThreadsInternal(tids, true);
}
@@ -2124,10 +2196,27 @@ public final class HintManagerService extends SystemService {
return mUid;
}
+ public boolean isTrackedBySf() {
+ synchronized (this) {
+ return mTrackedBySF;
+ }
+ }
+
+ public void setTrackedBySf(boolean tracked) {
+ synchronized (this) {
+ mTrackedBySF = tracked;
+ }
+ }
+
+
public int getTag() {
return mTag;
}
+ public Integer getSessionId() {
+ return mSessionId;
+ }
+
public long getTargetDurationNs() {
synchronized (this) {
return mTargetDurationNanos;
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 028ac57fc5a3..6f1810711b3a 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -409,11 +409,10 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public long getWakelockDurationMillis() {
synchronized (BatteryStatsImpl.this) {
- long rawRealtimeUs = mClock.uptimeMillis() * 1000;
- long batteryUptimeUs = getBatteryUptime(rawRealtimeUs);
- long screenOnTimeUs = getScreenOnTime(rawRealtimeUs,
+ long batteryUptimeUs = getBatteryUptime(mClock.uptimeMillis() * 1000);
+ long screenOnTimeUs = getScreenOnTime(mClock.elapsedRealtime() * 1000,
BatteryStats.STATS_SINCE_CHARGED);
- return (batteryUptimeUs - screenOnTimeUs) / 1000;
+ return Math.max(0, (batteryUptimeUs - screenOnTimeUs) / 1000);
}
}
@@ -437,8 +436,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- if (wakeLockTimeUs != 0) {
- callback.onUidWakelockDuration(u.getUid(), wakeLockTimeUs / 1000);
+ long wakelockTimeMs = wakeLockTimeUs / 1000;
+ if (wakelockTimeMs != 0) {
+ callback.onUidWakelockDuration(u.getUid(), wakelockTimeMs);
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java
index e36c9946531e..e3e4e1b28f43 100644
--- a/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java
@@ -108,14 +108,16 @@ class WakelockPowerStatsCollector extends PowerStatsCollector {
mWakelockDurationRetriever.retrieveUidWakelockDuration((uid, durationMs) -> {
if (!mFirstCollection) {
- long[] uidStats = mPowerStats.uidStats.get(uid);
- if (uidStats == null) {
- uidStats = new long[mDescriptor.uidStatsArrayLength];
- mPowerStats.uidStats.put(uid, uidStats);
+ long diffMs = Math.max(0, durationMs - mLastUidWakelockDurations.get(uid));
+ if (diffMs != 0) {
+ long[] uidStats = mPowerStats.uidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new long[mDescriptor.uidStatsArrayLength];
+ mPowerStats.uidStats.put(uid, uidStats);
+ }
+
+ mStatsLayout.setUidUsageDuration(uidStats, diffMs);
}
-
- mStatsLayout.setUidUsageDuration(uidStats,
- Math.max(0, durationMs - mLastUidWakelockDurations.get(uid)));
}
mLastUidWakelockDurations.put(uid, durationMs);
});
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index a15360760e32..7fb57085fb35 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -80,13 +80,25 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
private void initFeatures(boolean enabled) {
if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) {
+ try {
mHooks.add(new DisallowInstallUnknownSourcesAdvancedProtectionHook(mContext, enabled));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to initialize DisallowInstallUnknownSources", e);
+ }
}
if (android.security.Flags.aapmFeatureMemoryTaggingExtension()) {
+ try {
mHooks.add(new MemoryTaggingExtensionHook(mContext, enabled));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to initialize MemoryTaggingExtension", e);
+ }
}
if (android.security.Flags.aapmFeatureDisableCellular2g()) {
+ try {
mHooks.add(new DisallowCellular2GAdvancedProtectionHook(mContext, enabled));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to initialize DisallowCellular2g", e);
+ }
}
}
@@ -278,8 +290,13 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
for (int i = 0; i < mHooks.size(); i++) {
AdvancedProtectionHook feature = mHooks.get(i);
- if (feature.isAvailable()) {
- feature.onAdvancedProtectionChanged(enabled);
+ try {
+ if (feature.isAvailable()) {
+ feature.onAdvancedProtectionChanged(enabled);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to call hook for feature "
+ + feature.getFeature().getId(), e);
}
}
synchronized (mCallbacks) {
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 9e75cf2fc3f3..15c3099511f9 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -86,6 +86,8 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@GuardedBy("mLock")
private Status mEndStatusRequest;
@GuardedBy("mLock")
+ private boolean mEndedByVendor;
+ @GuardedBy("mLock")
private long mStartTime; // for debugging
@GuardedBy("mLock")
private long mEndUptime;
@@ -119,14 +121,15 @@ final class VendorVibrationSession extends IVibrationSession.Stub
public void finishSession() {
// Do not abort session in HAL, wait for ongoing vibration requests to complete.
// This might take a while to end the session, but it can be aborted by cancelSession.
- requestEndSession(Status.FINISHED, /* shouldAbort= */ false);
+ requestEndSession(Status.FINISHED, /* shouldAbort= */ false, /* isVendorRequest= */ true);
}
@Override
public void cancelSession() {
// Always abort session in HAL while cancelling it.
// This might be triggered after finishSession was already called.
- requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true);
+ requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true,
+ /* isVendorRequest= */ true);
}
@Override
@@ -158,7 +161,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
public DebugInfo getDebugInfo() {
synchronized (mLock) {
return new DebugInfoImpl(mStatus, mCallerInfo, mCreateUptime, mCreateTime, mStartTime,
- mEndUptime, mEndTime, mVibrations);
+ mEndUptime, mEndTime, mEndedByVendor, mVibrations);
}
}
@@ -172,13 +175,15 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void onCancel() {
Slog.d(TAG, "Cancellation signal received, cancelling vibration session...");
- requestEnd(Status.CANCELLED_BY_USER, /* endedBy= */ null, /* immediate= */ false);
+ requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true,
+ /* isVendorRequest= */ true);
}
@Override
public void binderDied() {
Slog.d(TAG, "Binder died, cancelling vibration session...");
- requestEnd(Status.CANCELLED_BINDER_DIED, /* endedBy= */ null, /* immediate= */ false);
+ requestEndSession(Status.CANCELLED_BINDER_DIED, /* shouldAbort= */ true,
+ /* isVendorRequest= */ false);
}
@Override
@@ -207,7 +212,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
// All requests to end a session should abort it to stop ongoing vibrations, even if
// immediate flag is false. Only the #finishSession API will not abort and wait for
// session vibrations to complete, which might take a long time.
- requestEndSession(status, /* shouldAbort= */ true);
+ requestEndSession(status, /* shouldAbort= */ true, /* isVendorRequest= */ false);
}
@Override
@@ -224,7 +229,8 @@ final class VendorVibrationSession extends IVibrationSession.Stub
public void notifySessionCallback() {
synchronized (mLock) {
// If end was not requested then the HAL has cancelled the session.
- maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON);
+ maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON,
+ /* isVendorRequest= */ false);
maybeSetStatusToRequestedLocked();
clearVibrationConductor();
}
@@ -335,10 +341,10 @@ final class VendorVibrationSession extends IVibrationSession.Stub
}
}
- private void requestEndSession(Status status, boolean shouldAbort) {
+ private void requestEndSession(Status status, boolean shouldAbort, boolean isVendorRequest) {
boolean shouldTriggerSessionHook = false;
synchronized (mLock) {
- maybeSetEndRequestLocked(status);
+ maybeSetEndRequestLocked(status, isVendorRequest);
if (isStarted()) {
// Always trigger session hook after it has started, in case new request aborts an
// already finishing session. Wait for HAL callback before actually ending here.
@@ -354,12 +360,13 @@ final class VendorVibrationSession extends IVibrationSession.Stub
}
@GuardedBy("mLock")
- private void maybeSetEndRequestLocked(Status status) {
+ private void maybeSetEndRequestLocked(Status status, boolean isVendorRequest) {
if (mEndStatusRequest != null) {
// End already requested, keep first requested status and time.
return;
}
mEndStatusRequest = status;
+ mEndedByVendor = isVendorRequest;
mEndTime = System.currentTimeMillis();
mEndUptime = SystemClock.uptimeMillis();
if (mConductor != null) {
@@ -442,15 +449,18 @@ final class VendorVibrationSession extends IVibrationSession.Stub
private final long mStartTime;
private final long mEndTime;
private final long mDurationMs;
+ private final boolean mEndedByVendor;
DebugInfoImpl(Status status, CallerInfo callerInfo, long createUptime, long createTime,
- long startTime, long endUptime, long endTime, List<DebugInfo> vibrations) {
+ long startTime, long endUptime, long endTime, boolean endedByVendor,
+ List<DebugInfo> vibrations) {
mStatus = status;
mCallerInfo = callerInfo;
mCreateUptime = createUptime;
mCreateTime = createTime;
mStartTime = startTime;
mEndTime = endTime;
+ mEndedByVendor = endedByVendor;
mDurationMs = endUptime > 0 ? endUptime - createUptime : -1;
mVibrations = vibrations == null ? new ArrayList<>() : new ArrayList<>(vibrations);
}
@@ -478,6 +488,15 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
+ if (mStartTime > 0) {
+ // Only log sessions that have started.
+ statsLogger.logVibrationVendorSessionStarted(mCallerInfo.uid);
+ statsLogger.logVibrationVendorSessionVibrations(mCallerInfo.uid,
+ mVibrations.size());
+ if (!mEndedByVendor) {
+ statsLogger.logVibrationVendorSessionInterrupted(mCallerInfo.uid);
+ }
+ }
for (DebugInfo vibration : mVibrations) {
vibration.logMetrics(statsLogger);
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 27f92b2080e6..2bf44981e6d5 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -25,6 +25,7 @@ import android.os.CombinedVibration;
import android.os.IBinder;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
@@ -211,6 +212,11 @@ abstract class Vibration {
public void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale);
statsLogger.writeVibrationReportedAsync(mStatsInfo);
+ if (Flags.vendorVibrationEffects()) {
+ // Log effect as it was originally requested.
+ statsLogger.logVibrationCountAndSizeIfVendorEffect(mCallerInfo.uid,
+ mOriginalEffect != null ? mOriginalEffect : mPlayedEffect);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
index e9c38940601c..08da43d8f0e0 100644
--- a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
@@ -16,8 +16,12 @@
package com.android.server.vibrator;
+import android.annotation.Nullable;
+import android.os.CombinedVibration;
import android.os.Handler;
+import android.os.Parcel;
import android.os.SystemClock;
+import android.os.VibrationEffect;
import android.util.Slog;
import android.view.HapticFeedbackConstants;
@@ -58,6 +62,16 @@ public class VibratorFrameworkStatsLogger {
"vibrator.value_vibration_adaptive_haptic_scale",
new Histogram.UniformOptions(20, 0, 2));
+ // Sizes in [1KB, ~4.5MB) defined by scaled buckets.
+ private static final Histogram sVibrationVendorEffectSizeHistogram = new Histogram(
+ "vibrator.value_vibration_vendor_effect_size",
+ new Histogram.ScaledRangeOptions(25, 0, 1, 1.4f));
+
+ // Session vibration count in [0, ~840) defined by scaled buckets.
+ private static final Histogram sVibrationVendorSessionVibrationsHistogram = new Histogram(
+ "vibrator.value_vibration_vendor_session_vibrations",
+ new Histogram.ScaledRangeOptions(20, 0, 1, 1.4f));
+
private final Object mLock = new Object();
private final Handler mHandler;
private final long mVibrationReportedLogIntervalMillis;
@@ -201,4 +215,88 @@ public class VibratorFrameworkStatsLogger {
Counter.logIncrementWithUid("vibrator.value_perform_haptic_feedback_keyboard", uid);
}
}
+
+ /** Logs when a vendor vibration session successfully started. */
+ public void logVibrationVendorSessionStarted(int uid) {
+ Counter.logIncrementWithUid("vibrator.value_vibration_vendor_session_started", uid);
+ }
+
+ /**
+ * Logs when a vendor vibration session is interrupted by the platform.
+ *
+ * <p>A vendor session is interrupted if it has successfully started and its end was not
+ * requested by the vendor. This could be the vibrator service interrupting an ongoing session,
+ * the vibrator HAL triggering the session completed callback early.
+ */
+ public void logVibrationVendorSessionInterrupted(int uid) {
+ Counter.logIncrementWithUid("vibrator.value_vibration_vendor_session_interrupted", uid);
+ }
+
+ /** Logs the number of vibrations requested for a single vendor vibration session. */
+ public void logVibrationVendorSessionVibrations(int uid, int vibrationCount) {
+ sVibrationVendorSessionVibrationsHistogram.logSampleWithUid(uid, vibrationCount);
+ }
+
+ /**
+ * Logs if given vibration contains at least one {@link VibrationEffect.VendorEffect}.
+ *
+ * <p>Each {@link VibrationEffect.VendorEffect} will also log the parcel data size for the
+ * {@link VibrationEffect.VendorEffect#getVendorData()} it holds.
+ */
+ public void logVibrationCountAndSizeIfVendorEffect(int uid,
+ @Nullable CombinedVibration vibration) {
+ if (vibration == null) {
+ return;
+ }
+ boolean hasVendorEffects = logVibrationSizeOfVendorEffects(uid, vibration);
+ if (hasVendorEffects) {
+ // Increment CombinedVibration with one or more vendor effects only once.
+ Counter.logIncrementWithUid("vibrator.value_vibration_vendor_effect_requests", uid);
+ }
+ }
+
+ private static boolean logVibrationSizeOfVendorEffects(int uid, CombinedVibration vibration) {
+ if (vibration instanceof CombinedVibration.Mono mono) {
+ if (mono.getEffect() instanceof VibrationEffect.VendorEffect effect) {
+ logVibrationVendorEffectSize(uid, effect);
+ return true;
+ }
+ return false;
+ }
+ if (vibration instanceof CombinedVibration.Stereo stereo) {
+ boolean hasVendorEffects = false;
+ for (int i = 0; i < stereo.getEffects().size(); i++) {
+ if (stereo.getEffects().valueAt(i) instanceof VibrationEffect.VendorEffect effect) {
+ logVibrationVendorEffectSize(uid, effect);
+ hasVendorEffects = true;
+ }
+ }
+ return hasVendorEffects;
+ }
+ if (vibration instanceof CombinedVibration.Sequential sequential) {
+ boolean hasVendorEffects = false;
+ for (int i = 0; i < sequential.getEffects().size(); i++) {
+ hasVendorEffects |= logVibrationSizeOfVendorEffects(uid,
+ sequential.getEffects().get(i));
+ }
+ return hasVendorEffects;
+ }
+ // Unknown combined vibration, skip metrics.
+ return false;
+ }
+
+ private static void logVibrationVendorEffectSize(int uid, VibrationEffect.VendorEffect effect) {
+ int dataSize;
+ Parcel vendorData = Parcel.obtain();
+ try {
+ // Measure data size as it'll be sent to the HAL via binder, not the serialization size.
+ // PersistableBundle creates an XML representation for the data in writeToStream, so it
+ // might be larger than the actual data that is transferred between processes.
+ effect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
+ dataSize = vendorData.dataSize();
+ } finally {
+ vendorData.recycle();
+ }
+ sVibrationVendorEffectSizeHistogram.logSampleWithUid(uid, dataSize);
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index cc163db4dc36..ae726c15ed79 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1197,7 +1197,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
new VendorVibrationSession.DebugInfoImpl(status, callerInfo,
SystemClock.uptimeMillis(), System.currentTimeMillis(),
/* startTime= */ 0, /* endUptime= */ 0, /* endTime= */ 0,
- /* vibrations= */ null));
+ /* endedByVendor= */ false, /* vibrations= */ null));
}
private void logAndRecordVibration(DebugInfo info) {
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 92ce251c4293..179866153b5a 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -34,6 +34,7 @@ import android.util.Log;
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactoryProvider;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewZygote;
@@ -246,6 +247,11 @@ public class SystemImpl implements SystemInterface {
}
@Override
+ public boolean isCompatibleImplementationPackage(PackageInfo packageInfo) {
+ return WebViewFactoryProvider.isCompatibleImplementationPackage(packageInfo);
+ }
+
+ @Override
public List<UserPackage> getPackageInfoForProviderAllUsers(WebViewProviderInfo configInfo) {
return UserPackage.getPackageInfosAllUsers(mContext, configInfo.packageName, PACKAGE_FLAGS);
}
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 67105542e6ef..d9e1a3a5e02d 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -47,6 +47,9 @@ public interface SystemInterface {
boolean systemIsDebuggable();
PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException;
+ /** Check if the given package is a compatible WebView implementation for the OS. */
+ boolean isCompatibleImplementationPackage(PackageInfo packageInfo);
+
/**
* Get the PackageInfos of all users for the package represented by {@param configInfo}.
* @return an array of UserPackages for a certain package, each UserPackage being belonging to a
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index a5a02cdedf97..9e8dc2690a9d 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -27,6 +27,7 @@ import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactoryProvider;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -79,7 +80,7 @@ class WebViewUpdateServiceImpl2 {
private static final long NS_PER_MS = 1000000;
private static final int VALIDITY_OK = 0;
- private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
+ private static final int VALIDITY_OS_INCOMPATIBLE = 1;
private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
@@ -587,9 +588,9 @@ class WebViewUpdateServiceImpl2 {
}
private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
- // Ensure the provider targets this framework release (or a later one).
- if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
- return VALIDITY_INCORRECT_SDK_VERSION;
+ // Ensure the provider is compatible with this framework release.
+ if (!mSystemInterface.isCompatibleImplementationPackage(packageInfo)) {
+ return VALIDITY_OS_INCOMPATIBLE;
}
if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
&& !mSystemInterface.systemIsDebuggable()) {
@@ -712,7 +713,8 @@ class WebViewUpdateServiceImpl2 {
}
pw.println(
TextUtils.formatSimple(
- " Minimum targetSdkVersion: %d", UserPackage.MINIMUM_SUPPORTED_SDK));
+ " %s",
+ WebViewFactoryProvider.describeCompatibleImplementationPackage()));
pw.println(
TextUtils.formatSimple(
" Minimum WebView version code: %d", mMinimumVersionCode));
@@ -786,8 +788,8 @@ class WebViewUpdateServiceImpl2 {
private static String getInvalidityReason(int invalidityReason) {
switch (invalidityReason) {
- case VALIDITY_INCORRECT_SDK_VERSION:
- return "SDK version too low";
+ case VALIDITY_OS_INCOMPATIBLE:
+ return "Not compatible with this OS version";
case VALIDITY_INCORRECT_VERSION_CODE:
return "Version code too low";
case VALIDITY_INCORRECT_SIGNATURE:
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 70a8f563275f..a077a0b9a2ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2054,6 +2054,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
break;
}
}
+ long timeRemaining = endTime - System.currentTimeMillis();
+ mWindowManager.mSnapshotController.mTaskSnapshotController.waitFlush(timeRemaining);
// Force checkReadyForSleep to complete.
checkReadyForSleepLocked(false /* allowDelay */);
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index ebb50db54693..a41832498880 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -173,7 +173,8 @@ final class AppCompatUtils {
appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
-
+ // TODO(b/379824541) Remove duplicate information.
+ appCompatTaskInfo.topActivityLetterboxBounds = bounds;
// We need to consider if letterboxed or pillarboxed.
// TODO(b/336807329) Encapsulate reachability logic
appCompatTaskInfo.setLetterboxDoubleTapEnabled(reachabilityOverrides
@@ -282,6 +283,7 @@ final class AppCompatUtils {
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxAppHeight = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+ info.topActivityLetterboxBounds = null;
info.cameraCompatTaskInfo.freeformCameraCompatMode =
CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
info.clearTopActivityFlags();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fc08a91278b8..4c55bf06510f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4267,7 +4267,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
}
final int imePolicy = mWmService.mDisplayWindowSettings.getImePolicyLocked(this);
- if (imePolicy == DISPLAY_IME_POLICY_FALLBACK_DISPLAY && forceDesktopMode()) {
+ if (imePolicy == DISPLAY_IME_POLICY_FALLBACK_DISPLAY
+ && isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
// If the display has not explicitly requested for the IME to be hidden then it shall
// show the IME locally.
return DISPLAY_IME_POLICY_LOCAL;
@@ -4275,10 +4276,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return imePolicy;
}
- boolean forceDesktopMode() {
- return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate();
- }
-
/** @see WindowManagerInternal#onToggleImeRequested */
void onShowImeRequested() {
if (mInputMethodWindow == null) {
@@ -4871,7 +4868,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** @return {@code true} if there is window to wait before enabling the screen. */
boolean shouldWaitForSystemDecorWindowsOnBoot() {
- if (!isDefaultDisplay && !supportsSystemDecorations()) {
+ if (!isDefaultDisplay && !isSystemDecorationsSupported()) {
// Nothing to wait because the secondary display doesn't support system decorations,
// there is no wallpaper, keyguard (status bar) or application (home) window to show
// during booting.
@@ -5750,22 +5747,42 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/**
* @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
*/
- boolean supportsSystemDecorations() {
- boolean forceDesktopModeOnDisplay = forceDesktopMode();
-
- if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) {
- // System decorations should not be forced on a rear display due to security policies.
- forceDesktopModeOnDisplay =
- forceDesktopModeOnDisplay && ((mDisplay.getFlags() & Display.FLAG_REAR) == 0);
+ boolean isSystemDecorationsSupported() {
+ if (mDisplayId == mWmService.mVr2dDisplayId) {
+ // VR virtual display will be used to run and render 2D app within a VR experience.
+ return false;
+ }
+ if (!isTrusted()) {
+ // Do not show system decorations on untrusted virtual display.
+ return false;
+ }
+ if (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
+ || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+ // This display is configured to show system decorations.
+ return true;
}
+ if (isPublicSecondaryDisplayWithDesktopModeForceEnabled()) {
+ if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) {
+ // System decorations should not be forced on a rear display due to security
+ // policies.
+ return (mDisplay.getFlags() & Display.FLAG_REAR) == 0;
+ }
+ // If the display is forced to desktop mode, treat it the same as it is configured to
+ // show system decorations.
+ return true;
+ }
+ return false;
+ }
- return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
- || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0
- || forceDesktopModeOnDisplay)
- // VR virtual display will be used to run and render 2D app within a VR experience.
- && mDisplayId != mWmService.mVr2dDisplayId
- // Do not show system decorations on untrusted virtual display.
- && isTrusted();
+ /**
+ * This is the development option to force enable desktop mode on all secondary public displays.
+ * When this is enabled, it also force enable system decorations on those displays.
+ *
+ * If we need a per-display config to enable desktop mode for production, that config should
+ * also check {@link #isSystemDecorationsSupported()} to avoid breaking any security policy.
+ */
+ boolean isPublicSecondaryDisplayWithDesktopModeForceEnabled() {
+ return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate();
}
/**
@@ -5776,7 +5793,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
boolean isHomeSupported() {
return (mWmService.mDisplayWindowSettings.isHomeSupportedLocked(this) && isTrusted())
- || supportsSystemDecorations();
+ || isSystemDecorationsSupported();
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 76e8a70768c1..659bb6784c89 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -659,7 +659,7 @@ public class DisplayPolicy {
}
} else {
mHasStatusBar = false;
- mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
+ mHasNavigationBar = mDisplayContent.isSystemDecorationsSupported();
}
mRefreshRatePolicy = new RefreshRatePolicy(mService,
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index df209ff4cf50..f53bc700de05 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -295,7 +295,7 @@ public class DisplayRotation {
&& mDeviceStateController
.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) {
mDisplayRotationCoordinator.setDefaultDisplayRotationChangedCallback(
- mDefaultDisplayRotationChangedCallback);
+ displayContent.getDisplayId(), mDefaultDisplayRotationChangedCallback);
}
if (isDefaultDisplay) {
@@ -445,7 +445,8 @@ public class DisplayRotation {
final boolean isTv = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
mDefaultFixedToUserRotation =
- (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode()
+ (isCar || isTv || mService.mIsPc
+ || mDisplayContent.isPublicSecondaryDisplayWithDesktopModeForceEnabled()
|| !mDisplayContent.shouldRotateWithContent())
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
@@ -1659,7 +1660,8 @@ public class DisplayRotation {
void removeDefaultDisplayRotationChangedCallback() {
if (DisplayRotationCoordinator.isSecondaryInternalDisplay(mDisplayContent)) {
- mDisplayRotationCoordinator.removeDefaultDisplayRotationChangedCallback();
+ mDisplayRotationCoordinator.removeDefaultDisplayRotationChangedCallback(
+ mDefaultDisplayRotationChangedCallback);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCoordinator.java b/services/core/java/com/android/server/wm/DisplayRotationCoordinator.java
index ae3787cffa23..01e1b1342989 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCoordinator.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCoordinator.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.Slog;
import android.view.Display;
import android.view.Surface;
@@ -40,6 +41,7 @@ class DisplayRotationCoordinator {
@Nullable
@VisibleForTesting
Runnable mDefaultDisplayRotationChangedCallback;
+ private int mCallbackDisplayId = Display.INVALID_DISPLAY;
@Surface.Rotation
private int mDefaultDisplayCurrentRotation;
@@ -68,12 +70,15 @@ class DisplayRotationCoordinator {
* Register a callback to be notified when the default display's rotation changes. Clients can
* query the default display's current rotation via {@link #getDefaultDisplayCurrentRotation()}.
*/
- void setDefaultDisplayRotationChangedCallback(@NonNull Runnable callback) {
- if (mDefaultDisplayRotationChangedCallback != null) {
- throw new UnsupportedOperationException("Multiple clients unsupported");
+ void setDefaultDisplayRotationChangedCallback(int displayId, @NonNull Runnable callback) {
+ if (mDefaultDisplayRotationChangedCallback != null && displayId != mCallbackDisplayId) {
+ throw new UnsupportedOperationException("Multiple clients unsupported"
+ + ". Incoming displayId: " + displayId
+ + ", existing displayId: " + mCallbackDisplayId);
}
mDefaultDisplayRotationChangedCallback = callback;
+ mCallbackDisplayId = displayId;
if (mDefaultDisplayCurrentRotation != mDefaultDisplayDefaultRotation) {
callback.run();
@@ -82,10 +87,17 @@ class DisplayRotationCoordinator {
/**
* Removes the callback that was added via
- * {@link #setDefaultDisplayRotationChangedCallback(Runnable)}.
+ * {@link #setDefaultDisplayRotationChangedCallback(int, Runnable)}.
*/
- void removeDefaultDisplayRotationChangedCallback() {
+ void removeDefaultDisplayRotationChangedCallback(@NonNull Runnable callback) {
+ if (callback != mDefaultDisplayRotationChangedCallback) {
+ Slog.w(TAG, "Attempted to remove non-matching callback."
+ + " DisplayId: " + mCallbackDisplayId);
+ return;
+ }
+
mDefaultDisplayRotationChangedCallback = null;
+ mCallbackDisplayId = Display.INVALID_DISPLAY;
}
static boolean isSecondaryInternalDisplay(@NonNull DisplayContent displayContent) {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index c87b811b4231..f6d05d08cb04 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -143,7 +143,7 @@ class DisplayWindowSettings {
}
// No record is present so use default windowing mode policy.
final boolean forceFreeForm = mService.mAtmService.mSupportsFreeformWindowManagement
- && (mService.mIsPc || dc.forceDesktopMode());
+ && (mService.mIsPc || dc.isPublicSecondaryDisplayWithDesktopModeForceEnabled());
if (forceFreeForm) {
return WindowConfiguration.WINDOWING_MODE_FREEFORM;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c89feb41e723..46312aff1fb6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2853,11 +2853,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void prepareForShutdown() {
+ mWindowManager.mSnapshotController.mTaskSnapshotController.prepareShutdown();
for (int i = 0; i < getChildCount(); i++) {
- final int displayId = getChildAt(i).mDisplayId;
- mWindowManager.mSnapshotController.mTaskSnapshotController
- .snapshotForShutdown(displayId);
- createSleepToken("shutdown", displayId);
+ createSleepToken("shutdown", getChildAt(i).mDisplayId);
}
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index bd8e8f4008de..8b63ecf7135f 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -103,12 +103,42 @@ class SnapshotPersistQueue {
}
/**
- * Write out everything in the queue because of shutdown.
+ * Prepare to enqueue all visible task snapshots because of shutdown.
*/
- void shutdown() {
+ void prepareShutdown() {
synchronized (mLock) {
mShutdown = true;
- mLock.notifyAll();
+ }
+ }
+
+ private boolean isQueueEmpty() {
+ synchronized (mLock) {
+ return mWriteQueue.isEmpty() || mQueueIdling || mPaused;
+ }
+ }
+
+ void waitFlush(long timeout) {
+ if (timeout <= 0) {
+ return;
+ }
+ final long endTime = System.currentTimeMillis() + timeout;
+ while (true) {
+ if (!isQueueEmpty()) {
+ long timeRemaining = endTime - System.currentTimeMillis();
+ if (timeRemaining > 0) {
+ synchronized (mLock) {
+ try {
+ mLock.wait(timeRemaining);
+ } catch (InterruptedException e) {
+ }
+ }
+ } else {
+ Slog.w(TAG, "Snapshot Persist Queue flush timed out");
+ break;
+ }
+ } else {
+ break;
+ }
}
}
@@ -139,7 +169,9 @@ class SnapshotPersistQueue {
mWriteQueue.addLast(item);
}
item.onQueuedLocked();
- ensureStoreQueueDepthLocked();
+ if (!mShutdown) {
+ ensureStoreQueueDepthLocked();
+ }
if (!mPaused) {
mLock.notifyAll();
}
@@ -213,6 +245,9 @@ class SnapshotPersistQueue {
if (!writeQueueEmpty && !mPaused) {
continue;
}
+ if (mShutdown && writeQueueEmpty) {
+ mLock.notifyAll();
+ }
try {
mQueueIdling = writeQueueEmpty;
mLock.wait();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 9fe3f7563902..c130931277fe 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -309,23 +309,31 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
/**
* Record task snapshots before shutdown.
*/
- void snapshotForShutdown(int displayId) {
+ void prepareShutdown() {
if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
return;
}
- final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
- if (displayContent == null) {
+ // Make write items run in a batch.
+ mPersister.mSnapshotPersistQueue.setPaused(true);
+ mPersister.mSnapshotPersistQueue.prepareShutdown();
+ for (int i = 0; i < mService.mRoot.getChildCount(); i++) {
+ mService.mRoot.getChildAt(i).forAllLeafTasks(task -> {
+ if (task.isVisible() && !task.isActivityTypeHome()) {
+ final TaskSnapshot snapshot = captureSnapshot(task);
+ if (snapshot != null) {
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ }
+ }
+ }, true /* traverseTopToBottom */);
+ }
+ mPersister.mSnapshotPersistQueue.setPaused(false);
+ }
+
+ void waitFlush(long timeout) {
+ if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
return;
}
- displayContent.forAllLeafTasks(task -> {
- if (task.isVisible() && !task.isActivityTypeHome()) {
- final TaskSnapshot snapshot = captureSnapshot(task);
- if (snapshot != null) {
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- }
- }
- }, true /* traverseTopToBottom */);
- mPersister.mSnapshotPersistQueue.shutdown();
+ mPersister.mSnapshotPersistQueue.waitFlush(timeout);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7a53ccf6110c..9e1509cf95cc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7700,7 +7700,7 @@ public class WindowManagerService extends IWindowManager.Stub
+ "not exist: %d", displayId);
return false;
}
- return displayContent.supportsSystemDecorations();
+ return displayContent.isSystemDecorationsSupported();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c42aa37d847b..ddff24d35232 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1851,14 +1851,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int applyKeyguardState(@NonNull WindowContainerTransaction.HierarchyOp hop) {
- int effects = TRANSACT_EFFECTS_NONE;
+ int effects = TRANSACT_EFFECTS_LIFECYCLE;
final KeyguardState keyguardState = hop.getKeyguardState();
if (keyguardState != null) {
- int displayId = keyguardState.getDisplayId();
boolean keyguardShowing = keyguardState.getKeyguardShowing();
boolean aodShowing = keyguardState.getAodShowing();
- mService.mKeyguardController.setKeyguardShown(displayId, keyguardShowing, aodShowing);
+ mService.setLockScreenShown(keyguardShowing, aodShowing);
}
return effects;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 82947377d01e..cebe790bb1b9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2757,7 +2757,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* Expands the given rectangle by the region of window resize handle for freeform window.
* @param inOutRect The rectangle to update.
*/
- private void adjustRegionInFreefromWindowMode(Rect inOutRect) {
+ private void adjustRegionInFreeformWindowMode(Rect inOutRect) {
if (!inFreeformWindowingMode()) {
return;
}
@@ -2808,7 +2808,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
}
- adjustRegionInFreefromWindowMode(mTmpRect);
+ adjustRegionInFreeformWindowMode(mTmpRect);
outRegion.set(mTmpRect);
cropRegionToRootTaskBoundsIfNeeded(outRegion);
}
@@ -3608,7 +3608,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
rootTask.getDimBounds(mTmpRect);
- adjustRegionInFreefromWindowMode(mTmpRect);
+ adjustRegionInFreeformWindowMode(mTmpRect);
region.op(mTmpRect, Region.Op.INTERSECT);
}
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 114fe324f016..945720544991 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,9 @@
<version>2</version>
<fqname>IAltitudeService/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.frameworks.devicestate</name>
+ <version>1</version>
+ <fqname>IDeviceStateService/default</fqname>
+ </hal>
</manifest>
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index aeb1ba93f049..de16b7ee8126 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -865,7 +865,8 @@ public class KeyValueBackupTaskTest {
runTask(task);
verify(mBackupManagerService).setWorkSource(null);
- verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)),
+ eq(false));
}
@Test
@@ -1101,7 +1102,8 @@ public class KeyValueBackupTaskTest {
runTask(task);
verify(agentMock.agentBinder).fail(any());
- verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)),
+ eq(false));
}
@Test
@@ -1423,7 +1425,7 @@ public class KeyValueBackupTaskTest {
assertCleansUpFiles(mTransport, PM_PACKAGE);
// We don't unbind PM
verify(mBackupAgentConnectionManager, never()).unbindAgent(
- argThat(applicationInfo(PM_PACKAGE)));
+ argThat(applicationInfo(PM_PACKAGE)), eq(false));
}
@Test
@@ -1445,7 +1447,7 @@ public class KeyValueBackupTaskTest {
runTask(task);
verify(mBackupAgentConnectionManager, never()).unbindAgent(
- argThat(applicationInfo(PM_PACKAGE)));
+ argThat(applicationInfo(PM_PACKAGE)), eq(false));
}
@Test
@@ -1651,7 +1653,7 @@ public class KeyValueBackupTaskTest {
InOrder inOrder = inOrder(agentMock.agent, mBackupAgentConnectionManager);
inOrder.verify(agentMock.agent).onQuotaExceeded(anyLong(), eq(1234L));
inOrder.verify(mBackupAgentConnectionManager).unbindAgent(
- argThat(applicationInfo(PACKAGE_1)));
+ argThat(applicationInfo(PACKAGE_1)), eq(false));
}
@Test
@@ -2983,7 +2985,8 @@ public class KeyValueBackupTaskTest {
private void assertCleansUpFilesAndAgent(TransportData transport, PackageData packageData) {
assertCleansUpFiles(transport, packageData);
- verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(packageData)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(packageData)),
+ eq(false));
}
private void assertCleansUpFiles(TransportData transport, PackageData packageData) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
index 2461e9e79acf..8aaa72339c5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
@@ -119,6 +119,8 @@ public class BackupAgentConnectionManagerTest {
mTestApplicationInfo = new ApplicationInfo();
mTestApplicationInfo.packageName = TEST_PACKAGE;
+ mTestApplicationInfo.processName = TEST_PACKAGE;
+ mTestApplicationInfo.uid = Process.FIRST_APPLICATION_UID + 1;
mBackupAgentResult = null;
mTestThread = null;
@@ -134,8 +136,8 @@ public class BackupAgentConnectionManagerTest {
@Test
public void bindToAgentSynchronous_amReturnsFailure_returnsNullAndClearsPendingBackups()
throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(false);
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(false);
IBackupAgent result = mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
@@ -147,8 +149,8 @@ public class BackupAgentConnectionManagerTest {
@Test
public void bindToAgentSynchronous_agentDisconnectedCalled_returnsNullAndClearsPendingBackups()
throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(true);
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(true);
// This is so that IBackupAgent.Stub.asInterface() works.
when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
@@ -172,24 +174,7 @@ public class BackupAgentConnectionManagerTest {
@Test
public void bindToAgentSynchronous_agentConnectedCalled_returnsBackupAgent() throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(true);
- // This is so that IBackupAgent.Stub.asInterface() works.
- when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
- when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
-
- // This is going to block until it receives the callback so we need to run it on a
- // separate thread.
- Thread testThread = new Thread(() -> setBackupAgentResult(
- mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD)),
- "backup-agent-connection-manager-test");
- testThread.start();
- // Give the testThread a head start, otherwise agentConnected() might run before
- // bindToAgentSynchronous() is called.
- Thread.sleep(500);
- mConnectionManager.agentConnected(TEST_PACKAGE, mBackupAgentStub);
- testThread.join();
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_FULL);
assertThat(mBackupAgentResult).isEqualTo(mBackupAgentStub);
verify(mActivityManagerInternal, never()).clearPendingBackup(anyInt());
@@ -198,8 +183,8 @@ public class BackupAgentConnectionManagerTest {
@Test
public void bindToAgentSynchronous_unexpectedAgentConnected_doesNotReturnWrongAgent()
throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(true);
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(true);
// This is so that IBackupAgent.Stub.asInterface() works.
when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
@@ -390,11 +375,75 @@ public class BackupAgentConnectionManagerTest {
@Test
public void unbindAgent_callsAmUnbindBackupAgent() throws Exception {
- mConnectionManager.unbindAgent(mTestApplicationInfo);
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ false);
verify(mActivityManager).unbindBackupAgent(eq(mTestApplicationInfo));
}
+ @Test
+ public void unbindAgent_doNotAllowKill_doesNotKillApp() throws Exception {
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ false);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_isCoreApp_doesNotKillApp() throws Exception {
+ mTestApplicationInfo.uid = 1000;
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_notCurrentConnection_killsApp() throws Exception {
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager).killApplicationProcess(eq(TEST_PACKAGE), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_inRestrictedMode_killsApp() throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_FULL);
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager).killApplicationProcess(eq(TEST_PACKAGE), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_notInRestrictedMode_doesNotKillApp() throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_isRestore_noKillAfterRestore_doesNotKillApp()
+ throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_RESTORE);
+ mTestApplicationInfo.flags = 0;
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_isRestore_killAfterRestore_killsApp() throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_RESTORE);
+ mTestApplicationInfo.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager).killApplicationProcess(eq(TEST_PACKAGE), anyInt());
+ }
+
// Needed because variables can't be assigned directly inside lambdas in Java.
private void setBackupAgentResult(IBackupAgent result) {
mBackupAgentResult = result;
@@ -404,4 +453,23 @@ public class BackupAgentConnectionManagerTest {
private void setTestThread(Thread thread) {
mTestThread = thread;
}
+
+ private void bindAndConnectToTestAppAgent(int backupMode) throws Exception {
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(true);
+ // This is going to block until it receives the callback so we need to run it on a
+ // separate thread.
+ Thread testThread = new Thread(() -> setBackupAgentResult(
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo, backupMode,
+ BackupDestination.CLOUD)), "backup-agent-connection-manager-test");
+ testThread.start();
+ // Give the testThread a head start, otherwise agentConnected() might run before
+ // bindToAgentSynchronous() is called.
+ Thread.sleep(500);
+ when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
+ // This is so that IBackupAgent.Stub.asInterface() works.
+ when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
+ mConnectionManager.agentConnected(TEST_PACKAGE, mBackupAgentStub);
+ testThread.join();
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java
index a1937cec706c..9b8a7cca7358 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationManagerServiceTest.java
@@ -23,6 +23,8 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
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.MockitoAnnotations.initMocks;
@@ -33,20 +35,24 @@ import android.content.res.Resources;
import android.location.ILocationListener;
import android.location.LocationManagerInternal;
import android.location.LocationRequest;
+import android.location.flags.Flags;
import android.location.provider.ProviderRequest;
import android.os.IBinder;
import android.os.PowerManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.LocalServices;
+import com.android.server.location.fudger.LocationFudgerCache;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
import com.android.server.location.provider.AbstractLocationProvider;
import com.android.server.location.provider.LocationProviderManager;
+import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.google.common.util.concurrent.MoreExecutors;
@@ -54,6 +60,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -75,8 +82,12 @@ public class LocationManagerServiceTest {
private TestInjector mInjector;
private LocationManagerService mLocationManagerService;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Spy private FakeAbstractLocationProvider mProviderWithPermission;
@Spy private FakeAbstractLocationProvider mProviderWithoutPermission;
+ @Mock private ProxyPopulationDensityProvider mPopulationDensityProvider;
@Mock private ILocationListener mLocationListener;
@Mock private IBinder mBinder;
@Mock private Context mContext;
@@ -172,6 +183,32 @@ public class LocationManagerServiceTest {
}
@Test
+ public void testSetLocationFudgerCache_withFeatureFlagDisabled_isNotCalled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ LocationProviderManager manager = mock(LocationProviderManager.class);
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ mLocationManagerService.addLocationProviderManager(manager, /* provider = */ null);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ mLocationManagerService.setLocationFudgerCache(cache);
+
+ verify(manager, never()).setLocationFudgerCache(any());
+ }
+
+ @Test
+ public void testSetLocationFudgerCache_withFeatureFlagEnabled_isCalled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ LocationProviderManager manager = mock(LocationProviderManager.class);
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ mLocationManagerService.addLocationProviderManager(manager, /* provider = */ null);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ mLocationManagerService.setLocationFudgerCache(cache);
+
+ verify(manager).setLocationFudgerCache(cache);
+ }
+
+ @Test
public void testHasProvider_noPermission() {
assertThat(mLocationManagerService.hasProvider(PROVIDER_WITHOUT_PERMISSION)).isFalse();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
new file mode 100644
index 000000000000..04b82c4890af
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.fudger;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.Mockito.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 android.location.flags.Flags;
+import android.location.provider.IS2CellIdsCallback;
+import android.location.provider.IS2LevelCallback;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.location.geometry.S2CellIdUtils;
+import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS)
+public class LocationFudgerCacheTest {
+
+ private static final String TAG = "LocationFudgerCacheTest";
+
+ private static final long TIMES_SQUARE_S2_ID =
+ S2CellIdUtils.fromLatLngDegrees(40.758896, -73.985130);
+
+ private static final double[] POINT_IN_TIMES_SQUARE = {40.75889599346095, -73.9851300385147};
+
+ private static final double[] POINT_OUTSIDE_TIMES_SQUARE = {48.858093, 2.294694};
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Test
+ public void hasDefaultValue_isInitiallyFalse()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ assertThat(cache.hasDefaultValue()).isFalse();
+ }
+
+ @Test
+ public void hasDefaultValue_uponQueryError_isStillFalse()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onError();
+
+ assertThat(cache.hasDefaultValue()).isFalse();
+ }
+
+ @Test
+ public void hasDefaultValue_afterSuccessfulQuery_isTrue()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onResult(10);
+
+ assertThat(cache.hasDefaultValue()).isTrue();
+ }
+
+ @Test
+ public void locationFudgerCache_whenQueriedOutsideOfCache_returnsDefault()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+ int level = 10;
+ int defaultLevel = 2;
+ Long s2Cell = S2CellIdUtils.getParent(TIMES_SQUARE_S2_ID, level);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onResult(defaultLevel);
+
+ cache.addToCache(s2Cell);
+
+ assertThat(cache.getCoarseningLevel(POINT_OUTSIDE_TIMES_SQUARE[0],
+ POINT_OUTSIDE_TIMES_SQUARE[1])).isEqualTo(defaultLevel);
+ }
+
+ @Test
+ public void locationFudgerCache_whenQueriedValueIsCached_returnsCachedValue() {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+ int level = 10;
+ Long s2Cell = S2CellIdUtils.getParent(TIMES_SQUARE_S2_ID, level);
+
+ cache.addToCache(s2Cell);
+
+ assertThat(cache.getCoarseningLevel(POINT_IN_TIMES_SQUARE[0], POINT_IN_TIMES_SQUARE[1]))
+ .isEqualTo(level);
+ }
+
+ @Test
+ public void locationFudgerCache_whenStarting_queriesDefaultValue() {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ verify(provider).getDefaultCoarseningLevel(any());
+ }
+
+ @Test
+ public void locationFudgerCache_ifDidntGetDefaultValue_queriesItAgain() {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ verify(provider, times(1)).getDefaultCoarseningLevel(any());
+
+ cache.getCoarseningLevel(90.0, 0.0);
+
+ verify(provider, times(2)).getDefaultCoarseningLevel(any());
+ }
+
+ @Test
+ public void locationFudgerCache_ifReceivedDefaultValue_doesNotQueriesIt()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onResult(10);
+
+ cache.getCoarseningLevel(90.0, 0.0);
+
+ // Verify getDefaultCoarseningLevel did not get called again
+ verify(provider, times(1)).getDefaultCoarseningLevel(any());
+ }
+
+ @Test
+ public void locationFudgerCache_whenSuccessfullyQueriesDefaultValue_storesResult()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+ int level = 10;
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onResult(level);
+
+ // Query any uncached location
+ assertThat(cache.getCoarseningLevel(0.0, 0.0)).isEqualTo(level);
+ }
+
+ @Test
+ public void locationFudgerCache_whenQueryingDefaultValueFails_returnsDefault()
+ throws RemoteException {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(argumentCaptor.capture());
+
+ IS2LevelCallback cb = argumentCaptor.getValue();
+ cb.onError();
+
+ // Query any uncached location. The default value is 0
+ assertThat(cache.getCoarseningLevel(0.0, 0.0)).isEqualTo(0);
+ }
+
+ @Test
+ public void locationFudgerCache_whenQueryIsNotCached_queriesProvider() {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ cache.getCoarseningLevel(POINT_IN_TIMES_SQUARE[0], POINT_IN_TIMES_SQUARE[1]);
+
+ verify(provider).getCoarsenedS2Cell(eq(POINT_IN_TIMES_SQUARE[0]),
+ eq(POINT_IN_TIMES_SQUARE[1]), any());
+ }
+
+ @Test
+ public void locationFudgerCache_whenProviderIsQueried_resultIsCached() throws RemoteException {
+ double lat = POINT_IN_TIMES_SQUARE[0];
+ double lng = POINT_IN_TIMES_SQUARE[1];
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ int level = cache.getCoarseningLevel(lat, lng);
+ assertThat(level).isEqualTo(0); // default value
+
+ ArgumentCaptor<IS2CellIdsCallback> argumentCaptor = ArgumentCaptor.forClass(
+ IS2CellIdsCallback.class);
+ verify(provider).getCoarsenedS2Cell(eq(POINT_IN_TIMES_SQUARE[0]),
+ eq(POINT_IN_TIMES_SQUARE[1]), argumentCaptor.capture());
+
+ // Results from the proxy should set the cache
+ int expectedLevel = 4;
+ long leafCell = S2CellIdUtils.fromLatLngDegrees(lat, lng);
+ Long s2CellId = S2CellIdUtils.getParent(leafCell, expectedLevel);
+ IS2CellIdsCallback cb = argumentCaptor.getValue();
+ long[] answer = new long[] {s2CellId};
+ cb.onResult(answer);
+
+ int level2 = cache.getCoarseningLevel(lat, lng);
+ assertThat(level2).isEqualTo(expectedLevel);
+ }
+
+ @Test
+ public void locationFudgerCache_whenQueryIsCached_doesNotRefreshIt() {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ cache.addToCache(TIMES_SQUARE_S2_ID);
+
+ verify(provider, never()).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ }
+
+ @Test
+ public void locationFudgerCache_canContainUpToMaxSizeItems() {
+ // This test has two sequences of arrange-act-assert.
+ // The first checks that the cache correctly store up to MAX_CACHE_SIZE items.
+ // The second checks that any new element replaces the oldest in the cache.
+
+ // Arrange.
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+ int size = cache.MAX_CACHE_SIZE;
+
+ double[][] latlngs = new double[size][2];
+ long[] cells = new long[size];
+ int[] expectedLevels = new int[size];
+
+ for (int i = 0; i < size; i++) {
+ // Create arbitrary lat/lngs.
+ latlngs[i][0] = 10.0 * i;
+ latlngs[i][1] = 10.0 * i;
+
+ expectedLevels[i] = 10; // we set some arbitrary S2 level for each latlng.
+
+ long leafCell = S2CellIdUtils.fromLatLngDegrees(latlngs[i][0], latlngs[i][1]);
+ long s2CellId = S2CellIdUtils.getParent(leafCell, expectedLevels[i]);
+ cells[i] = s2CellId;
+ }
+
+ // Act.
+ cache.addToCache(cells);
+
+ // Assert: check that the cache contains these latlngs and returns the correct level.
+ for (int i = 0; i < size; i++) {
+ assertThat(cache.getCoarseningLevel(latlngs[i][0], latlngs[i][1]))
+ .isEqualTo(expectedLevels[i]);
+ }
+
+ // Second assertion: A new value evicts the oldest one.
+
+ // Arrange.
+ int expectedLevel = 25;
+ long leafCell = S2CellIdUtils.fromLatLngDegrees(-10.0, -180.0);
+ long s2CellId = S2CellIdUtils.getParent(leafCell, expectedLevel);
+
+ // Act.
+ cache.addToCache(s2CellId);
+
+ // Assert: the new point is in the cache.
+ assertThat(cache.getCoarseningLevel(-10.0, -180.0)).isEqualTo(expectedLevel);
+ // Assert: all but the oldest point are still in cache.
+ for (int i = 0; i < size - 1; i++) {
+ assertThat(cache.getCoarseningLevel(latlngs[i][0], latlngs[i][1]))
+ .isEqualTo(expectedLevels[i]);
+ }
+ // Assert: the oldest point has been evicted.
+ assertThat(cache.getCoarseningLevel(latlngs[size - 1][0], latlngs[size - 1][1]))
+ .isEqualTo(0);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index 4e9b6c78edfd..d58e772f991d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -22,14 +22,23 @@ import static com.android.server.location.LocationUtils.createLocation;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyDouble;
+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 android.location.Location;
+import android.location.flags.Flags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,6 +64,9 @@ public class LocationFudgerTest {
private LocationFudger mFudger;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
long seed = System.currentTimeMillis();
@@ -162,4 +174,64 @@ public class LocationFudgerTest {
input.getLongitude() + deltaYM / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR,
0);
}
+
+ @Test
+ public void testDensityBasedCoarsening_ifFeatureIsDisabled_cacheIsNotUsed() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ LocationFudgerCache cache = mock(LocationFudgerCache.class);
+
+ mFudger.setLocationFudgerCache(cache);
+
+ mFudger.createCoarse(createLocation("test", mRandom));
+
+ verify(cache, never()).getCoarseningLevel(anyDouble(), anyDouble());
+ }
+
+ @Test
+ public void testDensityBasedCoarsening_ifFeatureIsEnabledButNotDefault_cacheIsNotUsed() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ LocationFudgerCache cache = mock(LocationFudgerCache.class);
+ doReturn(false).when(cache).hasDefaultValue();
+
+ mFudger.setLocationFudgerCache(cache);
+
+ mFudger.createCoarse(createLocation("test", mRandom));
+
+ verify(cache, never()).getCoarseningLevel(anyDouble(), anyDouble());
+ }
+
+ @Test
+ public void testDensityBasedCoarsening_ifFeatureIsEnabledAndDefaultIsSet_cacheIsUsed() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ LocationFudgerCache cache = mock(LocationFudgerCache.class);
+ doReturn(true).when(cache).hasDefaultValue();
+
+ mFudger.setLocationFudgerCache(cache);
+
+ Location fine = createLocation("test", mRandom);
+ mFudger.createCoarse(fine);
+
+ // We can't verify that the coordinatese of "fine" are passed to the API due to the addition
+ // of the offset. We must use anyDouble().
+ verify(cache).getCoarseningLevel(anyDouble(), anyDouble());
+ }
+
+ @Test
+ public void testDensityBasedCoarsening_newAlgorithm_snapsToCenterOfS2Cell_testVector() {
+ // NB: a complete test vector is in
+ // frameworks/base/services/tests/mockingservicestests/src/com/android/server/...
+ // location/geometry/S2CellIdUtilsTest.java
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ // Arbitrary location in Times Square, NYC
+ double[] latLng = new double[] {40.758896, -73.985130};
+ int s2Level = 1;
+ // The level-2 S2 cell around this location is "8c", its center is:
+ double[] expected = { 21.037511025421814, -67.38013505195958 };
+
+ double[] center = mFudger.snapToCenterOfS2Cell(latLng[0], latLng[1], s2Level);
+
+ assertThat(center[0]).isEqualTo(expected[0]);
+ assertThat(center[1]).isEqualTo(expected[1]);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 09282646ff68..cd1990407806 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -40,6 +40,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -72,6 +73,7 @@ import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.flags.Flags;
import android.location.provider.IProviderRequestListener;
+import android.location.provider.IS2LevelCallback;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -98,8 +100,10 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.location.fudger.LocationFudgerCache;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
+import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;
import org.junit.After;
import org.junit.Before;
@@ -1432,6 +1436,72 @@ public class LocationProviderManagerTest {
PERMISSION_FINE)).isEqualTo(location);
}
+ @Test
+ public void testLocationFudger_withFlagDisabled_cacheIsNotSetAndOldAlgoIsUsed() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ createManager("some-name");
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ mManager.setLocationFudgerCache(cache);
+
+ Location test = new Location("any-provider");
+ mManager.getPermittedLocation(test, PERMISSION_COARSE);
+
+ verify(provider, never()).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ }
+
+ @Test
+ public void testLocationFudger_withFlagEnabledButNoDefaults_oldAlgoIsUsed()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ createManager("some-other-name");
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+
+ mManager.setLocationFudgerCache(cache);
+
+ ArgumentCaptor<IS2LevelCallback> captor = ArgumentCaptor.forClass(IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(captor.capture());
+
+ IS2LevelCallback cb = captor.getValue();
+
+ // Act: the provider didn't provide a default
+ cb.onError();
+
+ Location test = new Location("any-provider");
+ mManager.getPermittedLocation(test, PERMISSION_COARSE);
+
+ verify(provider, never()).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ }
+
+ @Test
+ public void testLocationFudger_withFlagEnabled_cacheIsSetAndNewAlgoIsUsed()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS);
+ createManager("some-other-name");
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+ int defaultLevel = 2;
+
+ mManager.setLocationFudgerCache(cache);
+
+ ArgumentCaptor<IS2LevelCallback> captor = ArgumentCaptor.forClass(IS2LevelCallback.class);
+ verify(provider).getDefaultCoarseningLevel(captor.capture());
+
+ IS2LevelCallback cb = captor.getValue();
+ cb.onResult(defaultLevel);
+
+ Location test = new Location("any-provider");
+ test.setLatitude(10.0);
+ test.setLongitude(20.0);
+ mManager.getPermittedLocation(test, PERMISSION_COARSE);
+
+ // We can't test that 10.0, 20.0 was passed due to the offset. We only test that a call
+ // happened.
+ verify(provider).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ }
+
@MediumTest
@Test
public void testEnableMsl_expectedBehavior() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 08fdaf44f913..ac535b35cdfd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -1729,7 +1729,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
public void onHandleForceStop_dontDoIt_packageEnabled_returnsTrue() {
setupShortcutTargetServices();
AccessibilityUserState userState = mA11yms.getCurrentUserState();
@@ -1752,7 +1751,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
public void onHandleForceStop_doIt_packageEnabled_returnsFalse() {
setupShortcutTargetServices();
AccessibilityUserState userState = mA11yms.getCurrentUserState();
@@ -1775,7 +1773,6 @@ public class AccessibilityManagerServiceTest {
}
@Test
- @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
public void onHandleForceStop_dontDoIt_packageNotEnabled_returnsFalse() {
PackageMonitor monitor = spy(mA11yms.getPackageMonitor());
when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM);
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 8914696d55da..d4f2dcc24af6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -448,4 +448,14 @@ public class AccessibilityServiceConnectionTest {
mConnection.binderDied();
assertThat(mConnection.getServiceInfo().flags & flag).isEqualTo(0);
}
+
+ @Test
+ public void setInputMethodEnabled_checksAccessWithProvidedImeIdAndUserId() {
+ final String imeId = "test_ime_id";
+ final int callingUserId = UserHandle.getCallingUserId();
+ mConnection.setInputMethodEnabled(imeId, true);
+
+ verify(mMockSecurityPolicy).canEnableDisableInputMethod(
+ eq(imeId), any(), eq(callingUserId));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
index 52b33db556e6..f371823473ef 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -51,9 +51,6 @@ import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.view.KeyEvent;
@@ -98,9 +95,6 @@ public class ProxyManagerTest {
private static final int STREAMED_CALLING_UID = 9876;
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private Context mMockContext;
@@ -243,7 +237,6 @@ public class ProxyManagerTest {
* app changes to the proxy device.
*/
@Test
- @RequiresFlagsEnabled(Flags.FLAG_PROXY_USE_APPS_ON_VIRTUAL_DEVICE_LISTENER)
public void testUpdateProxyOfRunningAppsChange_changedUidIsStreamedApp_propagatesChange() {
final VirtualDeviceManagerInternal localVdm =
Mockito.mock(VirtualDeviceManagerInternal.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 727d1b59646a..32578a7dc10f 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
@@ -2076,10 +2076,10 @@ public class VirtualDeviceManagerServiceTest {
private AssociationInfo createAssociationInfo(int associationId, String deviceProfile,
CharSequence displayName) {
return new AssociationInfo(associationId, /* userId= */ 0, /* packageName=*/ null,
- /* tag= */ null, MacAddress.BROADCAST_ADDRESS, displayName, deviceProfile,
+ MacAddress.BROADCAST_ADDRESS, displayName, deviceProfile,
/* associatedDevice= */ null, /* selfManaged= */ true,
/* notifyOnDeviceNearby= */ false, /* revoked= */ false, /* pending= */ false,
/* timeApprovedMs= */0, /* lastTimeConnectedMs= */0,
- /* systemDataSyncFlags= */ -1, /* deviceIcon= */ null);
+ /* systemDataSyncFlags= */ -1, /* deviceIcon= */ null, /* deviceId= */ null);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index ab5a5a9bf2fe..5127b2d11e44 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -24,9 +24,14 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
@@ -34,6 +39,7 @@ import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -336,6 +342,53 @@ public final class DeviceStateManagerServiceTest {
}
@Test
+ public void halRegisterUnregisterCallback() throws RemoteException {
+ IDeviceStateService halService = mService.getHalBinderService();
+ IDeviceStateListener halListener = new IDeviceStateListener.Stub() {
+ @Override
+ public void onDeviceStateChanged(DeviceStateConfiguration deviceState) { }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IDeviceStateListener.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IDeviceStateListener.HASH;
+ }
+ };
+
+ int errorCode = ErrorCode.OK;
+ try {
+ halService.unregisterListener(halListener);
+ } catch(ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+ errorCode = ErrorCode.OK;
+ try {
+ halService.unregisterListener(null);
+ } catch(ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+ halService.registerListener(halListener);
+
+ errorCode = ErrorCode.OK;
+ try {
+ halService.registerListener(halListener);
+ } catch (ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.ALREADY_EXISTS);
+
+ halService.unregisterListener(halListener);
+ }
+
+ @Test
public void registerCallback() throws RemoteException {
final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index dd4101e9796f..30dac9f3813d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -433,4 +433,21 @@ public class HdmiCecAtomLoggingTest {
.dsmStatusChanged(anyBoolean(), anyBoolean(),
eq(HdmiStatsEnums.LOG_REASON_DSM_SETTING_TOGGLED));
}
+
+ @Test
+ public void testPowerStateChangeOnActiveSourceLostToggled_writesAtom_logReasonSetting() {
+ mHdmiControlServiceSpy.onWakeUp(WAKE_UP_SCREEN_ON);
+ Mockito.clearInvocations(mHdmiCecAtomWriterSpy);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.writePowerStateChangeOnActiveSourceLostAtom(true);
+ mTestLooper.dispatchAll();
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .powerStateChangeOnActiveSourceLostChanged(eq(true),
+ eq(HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_SETTING), anyString(), anyInt(), anyInt());
+ verify(mHdmiCecAtomWriterSpy, never())
+ .powerStateChangeOnActiveSourceLostChanged(eq(true),
+ eq(HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_POP_UP), anyString(), anyInt(), anyInt());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 077bb03c8359..861e72d4ac79 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -95,9 +95,6 @@ public class HdmiCecLocalDevicePlaybackTest {
private boolean mActiveMediaSessionsPaused;
private FakePowerManagerInternalWrapper mPowerManagerInternal =
new FakePowerManagerInternalWrapper();
-
- private boolean mIsOnActiveSourceLostPopupActive;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -165,12 +162,12 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
@Override
void startHdmiCecActiveSourceLostActivity() {
- mIsOnActiveSourceLostPopupActive = true;
+ setIsActiveSourceLostPopupLaunched(true);
}
@Override
void dismissUiOnActiveSourceStatusRecovered() {
- mIsOnActiveSourceLostPopupActive = false;
+ setIsActiveSourceLostPopupLaunched(false);
}
};
mHdmiCecLocalDevicePlayback.init();
@@ -2389,7 +2386,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
}
@Test
@@ -2430,7 +2427,7 @@ public class HdmiCecLocalDevicePlaybackTest {
// Pop-up is not shown, playback device asserts active source since TV doesn't answer the
// request.
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2480,7 +2477,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mTestLooper.dispatchAll();
// Pop-up is not shown since playback device is active source.
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2532,7 +2529,7 @@ public class HdmiCecLocalDevicePlaybackTest {
// Pop-up is shown, playback device doesn't assert active source since active path is
// switched to a non-CEC device.
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(ADDR_INVALID);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
@@ -2569,7 +2566,7 @@ public class HdmiCecLocalDevicePlaybackTest {
});
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
@@ -2609,13 +2606,13 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.onCecMessage(activeSourceFromTv);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2664,13 +2661,13 @@ public class HdmiCecLocalDevicePlaybackTest {
// Pop-up is triggered.
mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(routingChangeToPlayback))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2711,7 +2708,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.onCecMessage(activeSourceFromTv);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
mHdmiControlService.oneTouchPlay(new IHdmiControlCallback() {
@Override
public void onComplete(int result) throws RemoteException {
@@ -2724,7 +2721,7 @@ public class HdmiCecLocalDevicePlaybackTest {
});
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2897,11 +2894,11 @@ public class HdmiCecLocalDevicePlaybackTest {
} else {
mTestLooper.moveTimeForward(TIMEOUT_MS);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
return;
}
}
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
mPowerManagerInternal.setIdleDuration(idleDuration);
mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index aee9f0f3b880..13a6e4cc7b30 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
public class TestSystemImpl implements SystemInterface {
private String mUserProvider = null;
@@ -36,6 +37,7 @@ public class TestSystemImpl implements SystemInterface {
Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
private final int mNumRelros;
private final boolean mIsDebuggable;
+ private Predicate<PackageInfo> mCompatibilityPredicate;
public static final int PRIMARY_USER_ID = 0;
@@ -45,6 +47,7 @@ public class TestSystemImpl implements SystemInterface {
mNumRelros = numRelros;
mIsDebuggable = isDebuggable;
mUsers.add(PRIMARY_USER_ID);
+ mCompatibilityPredicate = pi -> true;
}
public void addUser(int userId) {
@@ -129,6 +132,15 @@ public class TestSystemImpl implements SystemInterface {
}
@Override
+ public boolean isCompatibleImplementationPackage(PackageInfo packageInfo) {
+ return mCompatibilityPredicate.test(packageInfo);
+ }
+
+ public void setCompatibilityPredicate(Predicate<PackageInfo> predicate) {
+ mCompatibilityPredicate = predicate;
+ }
+
+ @Override
public List<UserPackage> getPackageInfoForProviderAllUsers(WebViewProviderInfo info) {
Map<Integer, PackageInfo> userPackages = mPackages.get(info.packageName);
List<UserPackage> ret = new ArrayList();
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 42eb60958d53..bf99b6af2345 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -25,10 +25,12 @@ import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Base64;
-import android.webkit.UserPackage;
+import android.webkit.Flags;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -175,8 +177,6 @@ public class WebViewUpdateServiceTest {
// no flag means invalid
p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
}
- // Default to this package being valid in terms of targetSdkVersion.
- p.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
return p;
}
@@ -1238,20 +1238,21 @@ public class WebViewUpdateServiceTest {
}
/**
- * Ensure that packages with a targetSdkVersion targeting the current platform are valid, and
+ * Ensure that packages with a targetSdkVersion targeting the correct platform are valid, and
* that packages targeting an older version are not valid.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_USE_B_ENTRY_POINT)
public void testTargetSdkVersionValidity() {
PackageInfo newSdkPackage = createPackageInfo("newTargetSdkPackage",
- true /* enabled */, true /* valid */, true /* installed */);
- newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ true /* enabled */, true /* valid */, true /* installed */);
+ newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
PackageInfo currentSdkPackage = createPackageInfo("currentTargetSdkPackage",
- true /* enabled */, true /* valid */, true /* installed */);
- currentSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK;
+ true /* enabled */, true /* valid */, true /* installed */);
+ currentSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
PackageInfo oldSdkPackage = createPackageInfo("oldTargetSdkPackage",
- true /* enabled */, true /* valid */, true /* installed */);
- oldSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK - 1;
+ true /* enabled */, true /* valid */, true /* installed */);
+ oldSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.S;
WebViewProviderInfo newSdkProviderInfo =
new WebViewProviderInfo(newSdkPackage.packageName, "", true, false, null);
@@ -1264,6 +1265,9 @@ public class WebViewUpdateServiceTest {
newSdkProviderInfo
};
setupWithPackages(packages);
+ // Mock the compatibility predicate, requiring T as targetSdkVersion.
+ mTestSystemImpl.setCompatibilityPredicate(
+ pi -> pi.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU);
// Start with the setting pointing to the invalid package
mTestSystemImpl.updateUserSetting(oldSdkPackage.packageName);
@@ -1280,6 +1284,54 @@ public class WebViewUpdateServiceTest {
1 /* first preparation phase */);
}
+ /**
+ * Ensure that packages with a versionCode new enough for the current platform are valid, and
+ * that older packages are not valid.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_USE_B_ENTRY_POINT)
+ public void testVersionCodeOSCompatValidity() {
+ PackageInfo newVersionPackage = createPackageInfo("newVersionPackage",
+ true /* enabled */, true /* valid */, true /* installed */);
+ newVersionPackage.setLongVersionCode(200L);
+ PackageInfo currentVersionPackage = createPackageInfo("currentVersionPackage",
+ true /* enabled */, true /* valid */, true /* installed */);
+ currentVersionPackage.setLongVersionCode(100L);
+ PackageInfo oldVersionPackage = createPackageInfo("oldVersionPackage",
+ true /* enabled */, true /* valid */, true /* installed */);
+ oldVersionPackage.setLongVersionCode(50L);
+
+ WebViewProviderInfo newVersionProviderInfo =
+ new WebViewProviderInfo(newVersionPackage.packageName, "", true, false, null);
+ WebViewProviderInfo currentVersionProviderInfo =
+ new WebViewProviderInfo(currentVersionPackage.packageName, "", true, false, null);
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ currentVersionProviderInfo,
+ new WebViewProviderInfo(oldVersionPackage.packageName, "", true, false, null),
+ newVersionProviderInfo
+ };
+ setupWithPackages(packages);
+ // Mock the compatibility predicate as requiring 100 as versionCode.
+ mTestSystemImpl.setCompatibilityPredicate(
+ pi -> pi.getLongVersionCode() >= 100L);
+ // Start with the setting pointing to the invalid package
+ mTestSystemImpl.updateUserSetting(oldVersionPackage.packageName);
+
+ mTestSystemImpl.setPackageInfo(newVersionPackage);
+ mTestSystemImpl.setPackageInfo(currentVersionPackage);
+ mTestSystemImpl.setPackageInfo(oldVersionPackage);
+
+ assertArrayEquals(
+ new WebViewProviderInfo[] { currentVersionProviderInfo, newVersionProviderInfo },
+ mWebViewUpdateServiceImpl.getValidWebViewPackages());
+
+ runWebViewBootPreparationOnMainSync();
+
+ checkPreparationPhasesForPackage(currentVersionPackage.packageName,
+ 1 /* first preparation phase */);
+ }
+
@Test
public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
String nonDefaultPackage = "nonDefaultPackage";
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index ec83e990a70d..194d48a80a65 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -272,7 +272,8 @@ public class VibratorManagerServiceTest {
for (VendorVibrationSession session : mPendingSessions) {
session.cancelSession();
}
- mTestLooper.dispatchAll();
+ // Dispatch and wait for all callbacks in test looper to be processed.
+ stopAutoDispatcherAndDispatchAll();
// Wait until pending vibrations end asynchronously.
for (HalVibration vibration : mPendingVibrations) {
vibration.waitForEnd();
@@ -292,8 +293,6 @@ public class VibratorManagerServiceTest {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
- // Ignore potential exceptions about the looper having never dispatched any messages.
- mTestLooper.stopAutoDispatchAndIgnoreExceptions();
if (mInputManagerGlobalSession != null) {
mInputManagerGlobalSession.close();
}
@@ -1240,24 +1239,27 @@ public class VibratorManagerServiceTest {
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void vibrate_withOngoingHigherImportanceSession_ignoresEffect() throws Exception {
+ public void vibrate_withOngoingHigherImportanceVendorSession_ignoresEffect() throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
+
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
verify(callback).onStarted(any(IVibrationSession.class));
HalVibration vibration = vibrateAndWaitUntilFinished(service,
VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
HAPTIC_FEEDBACK_ATTRS);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
assertThat(vibration.getStatus()).isEqualTo(Status.IGNORED_FOR_HIGHER_IMPORTANCE);
@@ -1330,24 +1332,28 @@ public class VibratorManagerServiceTest {
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void vibrate_withOngoingLowerImportanceSession_cancelsOngoingSession() throws Exception {
+ public void vibrate_withOngoingLowerImportanceVendorSession_cancelsOngoingSession()
+ throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
+
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
verify(callback).onStarted(any(IVibrationSession.class));
HalVibration vibration = vibrateAndWaitUntilFinished(service,
VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
HAPTIC_FEEDBACK_ATTRS);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
assertThat(vibration.getStatus()).isEqualTo(Status.FINISHED);
@@ -2393,16 +2399,16 @@ public class VibratorManagerServiceTest {
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void onExternalVibration_withOngoingHigherImportanceSession_ignoreNewVibration()
+ public void onExternalVibration_withOngoingHigherImportanceVendorSession_ignoreNewVibration()
throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
verify(callback).onStarted(any(IVibrationSession.class));
@@ -2413,6 +2419,9 @@ public class VibratorManagerServiceTest {
// External vibration is ignored.
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
+
// Session still running.
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
verify(callback, never()).onFinishing();
@@ -2476,16 +2485,16 @@ public class VibratorManagerServiceTest {
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void onExternalVibration_withOngoingLowerImportanceSession_cancelsOngoingSession()
+ public void onExternalVibration_withOngoingLowerImportanceVendorSession_cancelsOngoingSession()
throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
verify(callback).onStarted(any(IVibrationSession.class));
@@ -2494,7 +2503,9 @@ public class VibratorManagerServiceTest {
ExternalVibrationScale scale =
mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
// Session is cancelled.
assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
@@ -2780,9 +2791,17 @@ public class VibratorManagerServiceTest {
assertThrows("Expected starting session without feature flag to fail!",
UnsupportedOperationException.class,
() -> startSession(service, RINGTONE_ATTRS, callback, vibratorId));
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionStarted(anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionVibrations(anyInt(), anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
verify(callback, never()).onStarted(any(IVibrationSession.class));
verify(callback, never()).onFinishing();
verify(callback, never()).onFinished(anyInt());
@@ -2798,10 +2817,18 @@ public class VibratorManagerServiceTest {
IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
callback, vibratorId);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionStarted(anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionVibrations(anyInt(), anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
verify(callback, never()).onFinishing();
verify(callback)
.onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
@@ -2817,10 +2844,18 @@ public class VibratorManagerServiceTest {
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
/* callback= */ null, vibratorId);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session).isNull();
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionStarted(anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionVibrations(anyInt(), anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
}
@Test
@@ -2837,11 +2872,18 @@ public class VibratorManagerServiceTest {
int[] emptyIds = {};
session = startSession(service, RINGTONE_ATTRS, callback, emptyIds);
- assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
- mTestLooper.dispatchAll();
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
+ assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionStarted(anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionVibrations(anyInt(), anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
verify(callback, never()).onFinishing();
verify(callback, times(2))
.onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
@@ -2862,10 +2904,18 @@ public class VibratorManagerServiceTest {
doReturn(token).when(callback).asBinder();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 3);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionStarted(anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionVibrations(anyInt(), anyInt());
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
verify(callback, never()).onStarted(any(IVibrationSession.class));
verify(callback, never()).onFinishing();
verify(callback)
@@ -2882,7 +2932,9 @@ public class VibratorManagerServiceTest {
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2901,6 +2953,12 @@ public class VibratorManagerServiceTest {
assertThat(session.getStatus()).isEqualTo(Status.FINISHED);
verify(callback).onFinishing();
verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+
+ verify(mVibratorFrameworkStatsLoggerMock).logVibrationVendorSessionStarted(eq(UID));
+ verify(mVibratorFrameworkStatsLoggerMock)
+ .logVibrationVendorSessionVibrations(eq(UID), eq(0));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
}
@Test
@@ -2913,7 +2971,9 @@ public class VibratorManagerServiceTest {
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2925,6 +2985,12 @@ public class VibratorManagerServiceTest {
assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_BY_USER);
verify(callback).onFinishing();
verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+
+ verify(mVibratorFrameworkStatsLoggerMock).logVibrationVendorSessionStarted(eq(UID));
+ verify(mVibratorFrameworkStatsLoggerMock)
+ .logVibrationVendorSessionVibrations(eq(UID), eq(0));
+ verify(mVibratorFrameworkStatsLoggerMock, never())
+ .logVibrationVendorSessionInterrupted(anyInt());
}
@Test
@@ -2933,12 +2999,12 @@ public class VibratorManagerServiceTest {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1, 2);
VibratorManagerService service = createSystemReadyService();
- // Delay not applied when session is aborted.
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2959,12 +3025,12 @@ public class VibratorManagerServiceTest {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1, 2);
VibratorManagerService service = createSystemReadyService();
- // Delay not applied when session is aborted.
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2988,6 +3054,7 @@ public class VibratorManagerServiceTest {
throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1, 2);
+
VibratorManagerService service = createSystemReadyService();
ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
ArgumentCaptor.forClass(
@@ -2999,7 +3066,9 @@ public class VibratorManagerServiceTest {
IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
doReturn(token).when(callback).asBinder();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
verify(callback).onStarted(any(IVibrationSession.class));
@@ -3011,6 +3080,11 @@ public class VibratorManagerServiceTest {
assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_BY_UNKNOWN_REASON);
verify(callback).onFinishing();
verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_CANCELED));
+
+ verify(mVibratorFrameworkStatsLoggerMock).logVibrationVendorSessionStarted(eq(UID));
+ verify(mVibratorFrameworkStatsLoggerMock)
+ .logVibrationVendorSessionVibrations(eq(UID), eq(0));
+ verify(mVibratorFrameworkStatsLoggerMock).logVibrationVendorSessionInterrupted(anyInt());
}
@Test
@@ -3019,13 +3093,14 @@ public class VibratorManagerServiceTest {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
VendorVibrationSession session1 = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
VendorVibrationSession session2 = startSession(service, RINGTONE_ATTRS, callback, 1);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
verify(callback).onStarted(captor.capture());
@@ -3051,8 +3126,7 @@ public class VibratorManagerServiceTest {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{10, 10_000}, new int[]{128, 255}, -1);
@@ -3064,7 +3138,9 @@ public class VibratorManagerServiceTest {
service, TEST_TIMEOUT_MILLIS));
VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock, never())
.startSession(eq(session.getSessionId()), any(int[].class));
@@ -3083,8 +3159,7 @@ public class VibratorManagerServiceTest {
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{10, 10_000}, new int[]{128, 255}, -1);
@@ -3098,7 +3173,9 @@ public class VibratorManagerServiceTest {
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
vibration.waitForEnd();
assertTrue(waitUntil(s -> session.isStarted(), service, TEST_TIMEOUT_MILLIS));
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(vibration.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
@@ -3116,8 +3193,7 @@ public class VibratorManagerServiceTest {
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
IBinder firstToken = mock(IBinder.class);
IExternalVibrationController controller = mock(IExternalVibrationController.class);
@@ -3128,7 +3204,9 @@ public class VibratorManagerServiceTest {
mExternalVibratorService.onExternalVibrationStart(externalVibration);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// The external vibration should have been cancelled
@@ -3149,9 +3227,10 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3186,9 +3265,10 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3228,9 +3308,10 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3272,9 +3353,10 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3305,6 +3387,10 @@ public class VibratorManagerServiceTest {
assertThat(service.isVibrating(2)).isFalse();
verify(callback).onFinishing();
verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+
+ verify(mVibratorFrameworkStatsLoggerMock).logVibrationVendorSessionStarted(eq(UID));
+ verify(mVibratorFrameworkStatsLoggerMock)
+ .logVibrationVendorSessionVibrations(eq(UID), eq(1));
}
@Test
@@ -3317,9 +3403,10 @@ public class VibratorManagerServiceTest {
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3355,6 +3442,10 @@ public class VibratorManagerServiceTest {
assertThat(service.isVibrating(2)).isFalse();
verify(callback).onFinishing();
verify(callback).onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_SUCCESS));
+
+ verify(mVibratorFrameworkStatsLoggerMock).logVibrationVendorSessionStarted(eq(UID));
+ verify(mVibratorFrameworkStatsLoggerMock)
+ .logVibrationVendorSessionVibrations(eq(UID), eq(2));
}
@Test
@@ -3773,6 +3864,10 @@ public class VibratorManagerServiceTest {
when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds);
}
+ private IVibrationSessionCallback mockSessionCallbacks() {
+ return mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ }
+
private IVibrationSessionCallback mockSessionCallbacks(long delayToEndSessionMillis) {
Handler handler = new Handler(mTestLooper.getLooper());
ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
@@ -3925,6 +4020,13 @@ public class VibratorManagerServiceTest {
return predicateResult;
}
+ private void stopAutoDispatcherAndDispatchAll() {
+ // Stop auto-dispatcher thread and wait for it to finish processing any messages.
+ mTestLooper.stopAutoDispatchAndIgnoreExceptions();
+ // Dispatch any pending message left.
+ mTestLooper.dispatchAll();
+ }
+
private void grantPermission(String permission) {
when(mContextSpy.checkCallingOrSelfPermission(permission))
.thenReturn(PackageManager.PERMISSION_GRANTED);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
index 6dba96766b48..a9a1f93e5ab8 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
@@ -76,7 +76,7 @@ public class ConversionUtilTest {
var apiconfig = new SoundTrigger.RecognitionConfig.Builder()
.setCaptureRequested(true)
- .setAllowMultipleTriggers(false) // must be false
+ .setMultipleTriggersAllowed(false) // must be false
.setKeyphrases(keyphrases)
.setData(data)
.setAudioCapabilities(flags)
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index 50c2c2fd926c..a5b2cb39cfff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
import android.app.TaskInfo;
+import android.graphics.Rect;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
@@ -198,6 +199,34 @@ public class AppCompatUtilsTest extends WindowTestsBase {
});
}
+ @Test
+ public void testTopActivityLetterboxed_hasBounds() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ a.setIgnoreOrientationRequest(true);
+ a.configureTopActivityBounds(new Rect(20, 30, 520, 630));
+ });
+ robot.setIsLetterboxedForAspectRatioOnly(/* forAspectRatio */ true);
+
+
+ robot.checkTaskInfoTopActivityHasBounds(/* expected */ new Rect(20, 30, 520, 630));
+ });
+ }
+
+ @Test
+ public void testTopActivityNotLetterboxed_hasNoBounds() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ });
+
+ robot.checkTaskInfoTopActivityHasBounds(/* expected */ null);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -282,6 +311,11 @@ public class AppCompatUtilsTest extends WindowTestsBase {
.cameraCompatTaskInfo.freeformCameraCompatMode);
}
+ void checkTaskInfoTopActivityHasBounds(Rect bounds) {
+ Assert.assertEquals(bounds, getTopTaskInfo().appCompatTaskInfo
+ .topActivityLetterboxBounds);
+ }
+
void setCameraCompatTreatmentEnabledForActivity(boolean enabled) {
doReturn(enabled).when(activity().displayContent().mAppCompatCameraPolicy
.mCameraCompatFreeformPolicy).isTreatmentEnabledForActivity(
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 9408f907d058..9cbea2e2f0ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -601,12 +601,12 @@ public class DisplayContentTests extends WindowTestsBase {
TYPE_WALLPAPER, TYPE_APPLICATION);
// Verify not waiting for display without system decorations.
- doReturn(false).when(secondaryDisplay).supportsSystemDecorations();
+ doReturn(false).when(secondaryDisplay).isSystemDecorationsSupported();
assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
// Verify waiting for non-drawn windows on display with system decorations.
reset(secondaryDisplay);
- doReturn(true).when(secondaryDisplay).supportsSystemDecorations();
+ doReturn(true).when(secondaryDisplay).isSystemDecorationsSupported();
assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
// Verify not waiting for drawn windows on display with system decorations.
@@ -1865,7 +1865,6 @@ public class DisplayContentTests extends WindowTestsBase {
mRootWindowContainer.getDisplayRotationCoordinator();
final DisplayContent defaultDisplayContent = mDisplayContent;
final DisplayRotation defaultDisplayRotation = defaultDisplayContent.getDisplayRotation();
- coordinator.removeDefaultDisplayRotationChangedCallback();
DeviceStateController deviceStateController = mock(DeviceStateController.class);
when(deviceStateController.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay())
@@ -1922,7 +1921,6 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayRotationCoordinator coordinator =
mRootWindowContainer.getDisplayRotationCoordinator();
- coordinator.removeDefaultDisplayRotationChangedCallback();
DeviceStateController deviceStateController = mock(DeviceStateController.class);
when(deviceStateController.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay())
@@ -2263,25 +2261,25 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
- public void testForceDesktopMode() {
+ public void testIsPublicSecondaryDisplayWithDesktopModeForceEnabled() {
mWm.mForceDesktopModeOnExternalDisplays = true;
// Not applicable for default display
- assertFalse(mDefaultDisplay.forceDesktopMode());
+ assertFalse(mDefaultDisplay.isPublicSecondaryDisplayWithDesktopModeForceEnabled());
// Not applicable for private secondary display.
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.copyFrom(mDisplayInfo);
displayInfo.flags = FLAG_PRIVATE;
final DisplayContent privateDc = createNewDisplay(displayInfo);
- assertFalse(privateDc.forceDesktopMode());
+ assertFalse(privateDc.isPublicSecondaryDisplayWithDesktopModeForceEnabled());
// Applicable for public secondary display.
final DisplayContent publicDc = createNewDisplay();
- assertTrue(publicDc.forceDesktopMode());
+ assertTrue(publicDc.isPublicSecondaryDisplayWithDesktopModeForceEnabled());
// Make sure forceDesktopMode() is false when the force config is disabled.
mWm.mForceDesktopModeOnExternalDisplays = false;
- assertFalse(publicDc.forceDesktopMode());
+ assertFalse(publicDc.isPublicSecondaryDisplayWithDesktopModeForceEnabled());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCoordinatorTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCoordinatorTests.java
index 4557df0e9c98..266ffffabf15 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCoordinatorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCoordinatorTests.java
@@ -40,6 +40,9 @@ import org.junit.Test;
@Presubmit
public class DisplayRotationCoordinatorTests {
+ private static final int FIRST_DISPLAY_ID = 1;
+ private static final int SECOND_DISPLAY_ID = 2;
+
@NonNull
private final DisplayRotationCoordinator mCoordinator = new DisplayRotationCoordinator();
@@ -50,22 +53,45 @@ public class DisplayRotationCoordinatorTests {
}
@Test (expected = UnsupportedOperationException.class)
- public void testSecondRegistrationWithoutRemovingFirst() {
+ public void testSecondRegistrationWithoutRemovingFirstWhenDifferentDisplay() {
Runnable callback1 = mock(Runnable.class);
Runnable callback2 = mock(Runnable.class);
- mCoordinator.setDefaultDisplayRotationChangedCallback(callback1);
- mCoordinator.setDefaultDisplayRotationChangedCallback(callback2);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback1);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(SECOND_DISPLAY_ID, callback2);
assertEquals(callback1, mCoordinator.mDefaultDisplayRotationChangedCallback);
}
@Test
+ public void testSecondRegistrationWithoutRemovingFirstWhenSameDisplay() {
+ Runnable callback1 = mock(Runnable.class);
+ Runnable callback2 = mock(Runnable.class);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback1);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback2);
+ assertEquals(callback2, mCoordinator.mDefaultDisplayRotationChangedCallback);
+ }
+
+ @Test
+ public void testRemoveIncorrectRegistration() {
+ Runnable callback1 = mock(Runnable.class);
+ Runnable callback2 = mock(Runnable.class);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback1);
+ mCoordinator.removeDefaultDisplayRotationChangedCallback(callback2);
+ assertEquals(callback1, mCoordinator.mDefaultDisplayRotationChangedCallback);
+
+ // FIRST_DISPLAY_ID is still able to register another callback because the previous
+ // removal should not have succeeded.
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback2);
+ assertEquals(callback2, mCoordinator.mDefaultDisplayRotationChangedCallback);
+ }
+
+ @Test
public void testSecondRegistrationAfterRemovingFirst() {
Runnable callback1 = mock(Runnable.class);
- mCoordinator.setDefaultDisplayRotationChangedCallback(callback1);
- mCoordinator.removeDefaultDisplayRotationChangedCallback();
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback1);
+ mCoordinator.removeDefaultDisplayRotationChangedCallback(callback1);
Runnable callback2 = mock(Runnable.class);
- mCoordinator.setDefaultDisplayRotationChangedCallback(callback2);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(SECOND_DISPLAY_ID, callback2);
mCoordinator.onDefaultDisplayRotationChanged(Surface.ROTATION_90);
verify(callback2).run();
@@ -75,7 +101,7 @@ public class DisplayRotationCoordinatorTests {
@Test
public void testRegisterThenDefaultDisplayRotationChanged() {
Runnable callback = mock(Runnable.class);
- mCoordinator.setDefaultDisplayRotationChangedCallback(callback);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback);
assertEquals(Surface.ROTATION_0, mCoordinator.getDefaultDisplayCurrentRotation());
verify(callback, never()).run();
@@ -88,7 +114,7 @@ public class DisplayRotationCoordinatorTests {
public void testDefaultDisplayRotationChangedThenRegister() {
mCoordinator.onDefaultDisplayRotationChanged(Surface.ROTATION_90);
Runnable callback = mock(Runnable.class);
- mCoordinator.setDefaultDisplayRotationChangedCallback(callback);
+ mCoordinator.setDefaultDisplayRotationChangedCallback(FIRST_DISPLAY_ID, callback);
verify(callback).run();
assertEquals(Surface.ROTATION_90, mCoordinator.getDefaultDisplayCurrentRotation());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index b5953839ca8f..f795d93b2487 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -605,7 +605,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
@Test
public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
DisplayContent display = createNewDisplay();
- doReturn(true).when(display).supportsSystemDecorations();
+ doReturn(true).when(display).isSystemDecorationsSupported();
// Remove the current home root task if it exists so a new one can be created below.
TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
@@ -622,7 +622,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
DisplayContent display = createNewDisplay();
TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
- doReturn(false).when(display).supportsSystemDecorations();
+ doReturn(false).when(display).isSystemDecorationsSupported();
assertNull(taskDisplayArea.getRootHomeTask());
assertNull(taskDisplayArea.getOrCreateRootHomeTask());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 546b1ba66b08..ccce57a81e41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -204,13 +204,13 @@ class TestDisplayContent extends DisplayContent {
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
if (mSystemDecorations) {
- doReturn(true).when(newDisplay).supportsSystemDecorations();
+ doReturn(true).when(newDisplay).isSystemDecorationsSupported();
doReturn(true).when(displayPolicy).hasNavigationBar();
doReturn(true).when(displayPolicy).hasBottomNavigationBar();
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
- doReturn(false).when(newDisplay).supportsSystemDecorations();
+ doReturn(false).when(newDisplay).isSystemDecorationsSupported();
}
// Update the display policy to make the screen fully turned on so animation is allowed
displayPolicy.screenTurningOn(null /* screenOnListener */);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index fb031bd01673..01ff67400eb1 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -842,7 +842,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return;
}
- model.setRequested(config.isAllowMultipleTriggers());
+ model.setRequested(config.isMultipleTriggersAllowed());
// TODO: Remove this block if the lower layer supports multiple triggers.
if (model.isRequested()) {
updateRecognitionLocked(model, true);
@@ -964,7 +964,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
RecognitionConfig config = modelData.getRecognitionConfig();
if (config != null) {
// Whether we should continue by starting this again.
- modelData.setRequested(config.isAllowMultipleTriggers());
+ modelData.setRequested(config.isMultipleTriggersAllowed());
}
// TODO: Remove this block if the lower layer supports multiple triggers.
if (modelData.isRequested()) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 19a6ddc3a931..e0cdbddd2efb 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1445,7 +1445,7 @@ public class SoundTriggerService extends SystemService {
runOrAddOperation(new Operation(
// always execute:
() -> {
- if (!mRecognitionConfig.isAllowMultipleTriggers()) {
+ if (!mRecognitionConfig.isMultipleTriggersAllowed()) {
// Unregister this remoteService once op is done
synchronized (mCallbacksLock) {
mCallbacks.remove(mPuuid.getUuid());
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ad5d42ad5a68..024d7f580a53 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5251,6 +5251,36 @@ public class TelephonyManager {
}
/**
+ * Returns the Group Identifier Level 2 in hexadecimal format.
+ * @return the Group Identifier Level 2 for the SIM card.
+ * Return null if it is unavailable.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_GET_GROUP_ID_LEVEL2)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+ @SystemApi
+ @Nullable
+ public String getGroupIdLevel2() {
+ try {
+ IPhoneSubInfo info = getSubscriberInfoService();
+ if (info == null) {
+ return null;
+ }
+ return info.getGroupIdLevel2ForSubscriber(getSubId(), mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
* Returns the phone number string for line 1, for example, the MSISDN
* for a GSM phone for a particular subscription. Return null if it is unavailable.
* <p>
diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.java b/telephony/java/android/telephony/satellite/EarfcnRange.java
index 207b25d60d90..81c8ed326b64 100644
--- a/telephony/java/android/telephony/satellite/EarfcnRange.java
+++ b/telephony/java/android/telephony/satellite/EarfcnRange.java
@@ -19,6 +19,7 @@ package android.telephony.satellite;
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,7 +41,8 @@ import java.util.Objects;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class EarfcnRange implements Parcelable {
/**
@@ -74,6 +76,7 @@ public final class EarfcnRange implements Parcelable {
*
* @param startEarfcn The starting earfcn value.
* @param endEarfcn The ending earfcn value.
+ * @hide
*/
public EarfcnRange(@IntRange(from = 0, to = 65535) int startEarfcn,
@IntRange(from = 0, to = 65535) int endEarfcn) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
index c1a6ae850985..56c92d04410b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
+++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +35,8 @@ import java.util.Objects;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteAccessConfiguration implements Parcelable {
/**
* The list of satellites available at the current location.
@@ -54,6 +56,7 @@ public final class SatelliteAccessConfiguration implements Parcelable {
* @param satelliteInfos The list of {@link SatelliteInfo} objects representing the satellites
* accessible with this configuration.
* @param tagIdList The list of tag IDs associated with this configuration.
+ * @hide
*/
public SatelliteAccessConfiguration(@NonNull List<SatelliteInfo> satelliteInfos,
@NonNull List<Integer> tagIdList) {
@@ -61,12 +64,18 @@ public final class SatelliteAccessConfiguration implements Parcelable {
mTagIdList = tagIdList;
}
+ /**
+ * Constructor for {@link SatelliteAccessConfiguration}.
+ * @param in parcel used to create {@link SatelliteAccessConfiguration} object
+ * @hide
+ */
public SatelliteAccessConfiguration(Parcel in) {
mSatelliteInfoList = in.createTypedArrayList(SatelliteInfo.CREATOR);
mTagIdList = new ArrayList<>();
in.readList(mTagIdList, Integer.class.getClassLoader(), Integer.class);
}
+ @NonNull
public static final Creator<SatelliteAccessConfiguration> CREATOR =
new Creator<SatelliteAccessConfiguration>() {
@Override
diff --git a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
index bffb11f23d56..6291102cd6e3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
@@ -18,6 +18,8 @@ package android.telephony.satellite;
import android.annotation.FlaggedApi;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
import com.android.internal.telephony.flags.Flags;
@@ -27,19 +29,19 @@ import com.android.internal.telephony.flags.Flags;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SatelliteCommunicationAllowedStateCallback {
/**
* Telephony does not guarantee that whenever there is a change in communication allowed state,
* this API will be called. Telephony does its best to detect the changes and notify its
- * listeners accordingly.
+ * listeners accordingly. Satellite communication is allowed at a location when it is legally
+ * allowed by the local authority and satellite signal coverage is available.
*
- * @param isAllowed {@code true} means satellite allow state is changed,
- * {@code false} satellite allow state is not changed
- * @hide
+ * @param isAllowed {@code true} means satellite is allowed,
+ * {@code false} satellite is not allowed.
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteCommunicationAllowedStateChanged(boolean isAllowed);
/**
@@ -49,9 +51,7 @@ public interface SatelliteCommunicationAllowedStateCallback {
* the current location. When satellite is not allowed at
* the current location,
* {@code satelliteRegionalConfiguration} will be null.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
default void onSatelliteAccessConfigurationChanged(
@Nullable SatelliteAccessConfiguration satelliteAccessConfiguration) {};
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.java b/telephony/java/android/telephony/satellite/SatelliteInfo.java
index 1d5f613cc086..ebb4e9c99549 100644
--- a/telephony/java/android/telephony/satellite/SatelliteInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteInfo.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
@@ -36,8 +37,9 @@ import java.util.UUID;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public class SatelliteInfo implements Parcelable {
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+public final class SatelliteInfo implements Parcelable {
/**
* Unique identification number for the satellite.
* This ID is used to distinguish between different satellites in the network.
@@ -68,6 +70,9 @@ public class SatelliteInfo implements Parcelable {
*/
private final List<EarfcnRange> mEarfcnRangeList;
+ /**
+ * @hide
+ */
protected SatelliteInfo(Parcel in) {
ParcelUuid parcelUuid = in.readParcelable(
ParcelUuid.class.getClassLoader(), ParcelUuid.class);
@@ -89,6 +94,7 @@ public class SatelliteInfo implements Parcelable {
* @param bandList The list of frequency bandList supported by the satellite.
* @param earfcnRanges The list of {@link EarfcnRange} objects representing the EARFCN
* ranges supported by the satellite.
+ * @hide
*/
public SatelliteInfo(@NonNull UUID satelliteId, @NonNull SatellitePosition satellitePosition,
@NonNull List<Integer> bandList, @NonNull List<EarfcnRange> earfcnRanges) {
@@ -98,6 +104,7 @@ public class SatelliteInfo implements Parcelable {
mEarfcnRangeList = earfcnRanges;
}
+ @NonNull
public static final Creator<SatelliteInfo> CREATOR = new Creator<SatelliteInfo>() {
@Override
public SatelliteInfo createFromParcel(Parcel in) {
@@ -135,6 +142,10 @@ public class SatelliteInfo implements Parcelable {
/**
* Returns the position of the satellite.
+ * Position information of a geostationary satellite.
+ * This includes the longitude and altitude of the satellite.
+ * If the SatellitePosition is invalid,
+ * longitudeDegree and altitudeKm will be represented as DOUBLE.NaN.
*
* @return The {@link SatellitePosition} of the satellite.
*/
@@ -146,6 +157,8 @@ public class SatelliteInfo implements Parcelable {
/**
* Returns the list of frequency bands supported by the satellite.
*
+ * Refer specification 3GPP TS 36.101 for detailed information on frequency bands.
+ *
* @return The list of frequency bands.
*/
@NonNull
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index b96b6c53703c..db5689b7a208 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -755,6 +755,8 @@ public final class SatelliteManager {
* config_satellite_gateway_service_package
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED =
"android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
@@ -766,6 +768,8 @@ public final class SatelliteManager {
* config_satellite_gateway_service_package
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION =
"android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION";
/**
@@ -781,6 +785,8 @@ public final class SatelliteManager {
* }
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT =
"android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT";
@@ -3282,13 +3288,14 @@ public final class SatelliteManager {
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle the satellite supoprted state changed event.
*
- * @return The {@link SatelliteResult} result of the operation.
+ * @return The 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
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteResult public int registerForSupportedStateChanged(
@NonNull @CallbackExecutor Executor executor,
@@ -3330,11 +3337,11 @@ 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
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void unregisterForSupportedStateChanged(
@NonNull SatelliteSupportedStateCallback callback) {
Objects.requireNonNull(callback);
@@ -3363,11 +3370,13 @@ public final class SatelliteManager {
*
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle satellite communication allowed state changed event.
- * @return The {@link SatelliteResult} result of the operation.
+ * @return The 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
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteResult
public int registerForCommunicationAllowedStateChanged(
@@ -3422,8 +3431,9 @@ public final class SatelliteManager {
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void unregisterForCommunicationAllowedStateChanged(
@NonNull SatelliteCommunicationAllowedStateCallback callback) {
Objects.requireNonNull(callback);
@@ -3463,7 +3473,6 @@ public final class SatelliteManager {
*/
@RequiresPermission(allOf = {Manifest.permission.PACKAGE_USAGE_STATS,
Manifest.permission.MODIFY_PHONE_STATE})
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestSessionStats(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteSessionStats, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -3535,8 +3544,9 @@ public final class SatelliteManager {
* @throws SecurityException if the caller doesn't have required permission.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void requestSatelliteSubscriberProvisionStatus(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<List<SatelliteSubscriberProvisionStatus>,
@@ -3593,8 +3603,9 @@ public final class SatelliteManager {
* @throws SecurityException if the caller doesn't have required permission.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -3648,8 +3659,9 @@ public final class SatelliteManager {
* @throws SecurityException if the caller doesn't have required permission.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java b/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
index 5e56f84ab1a2..d7fa3c9265e0 100644
--- a/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
+++ b/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
@@ -16,10 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -28,6 +32,8 @@ import java.util.Objects;
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteModemEnableRequestAttributes implements Parcelable {
/** {@code true} to enable satellite and {@code false} to disable satellite */
@@ -47,6 +53,9 @@ public final class SatelliteModemEnableRequestAttributes implements Parcelable {
/** The subscription related info */
@NonNull private final SatelliteSubscriptionInfo mSatelliteSubscriptionInfo;
+ /**
+ * @hide
+ */
public SatelliteModemEnableRequestAttributes(boolean isEnabled, boolean isDemoMode,
boolean isEmergencyMode, @NonNull SatelliteSubscriptionInfo satelliteSubscriptionInfo) {
mIsEnabled = isEnabled;
@@ -76,6 +85,7 @@ public final class SatelliteModemEnableRequestAttributes implements Parcelable {
mSatelliteSubscriptionInfo.writeToParcel(dest, flags);
}
+ @NonNull
public static final Creator<SatelliteModemEnableRequestAttributes> CREATOR = new Creator<>() {
@Override
public SatelliteModemEnableRequestAttributes createFromParcel(Parcel in) {
@@ -114,19 +124,39 @@ public final class SatelliteModemEnableRequestAttributes implements Parcelable {
return Objects.hash(mIsEnabled, mIsDemoMode, mIsEmergencyMode, mSatelliteSubscriptionInfo);
}
+
+ /**
+ * Get whether satellite modem needs to be enabled or disabled.
+ * @return {@code true} if the request is to enable satellite, else {@code false} to disable
+ * satellite.
+ */
public boolean isEnabled() {
return mIsEnabled;
}
+ /**
+ * Get whether satellite modem is enabled for demo mode.
+ * @return {@code true} if the request is to enable demo mode, else {@code false}.
+ */
public boolean isDemoMode() {
return mIsDemoMode;
}
+ /**
+ * Get whether satellite modem is enabled for emergency mode.
+ * @return {@code true} if the request is to enable satellite for emergency mode,
+ * else {@code false}.
+ */
public boolean isEmergencyMode() {
return mIsEmergencyMode;
}
- @NonNull public SatelliteSubscriptionInfo getSatelliteSubscriptionInfo() {
+
+ /**
+ * Return subscription info related to satellite.
+ */
+ @NonNull
+ public SatelliteSubscriptionInfo getSatelliteSubscriptionInfo() {
return mSatelliteSubscriptionInfo;
}
}
diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.java b/telephony/java/android/telephony/satellite/SatellitePosition.java
index dd463e00ebb5..b8d138fe5c17 100644
--- a/telephony/java/android/telephony/satellite/SatellitePosition.java
+++ b/telephony/java/android/telephony/satellite/SatellitePosition.java
@@ -16,6 +16,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,8 +35,9 @@ import java.util.Objects;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public class SatellitePosition implements Parcelable {
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+public final class SatellitePosition implements Parcelable {
/**
* The longitude of the satellite in degrees, ranging from -180 to 180 degrees
@@ -51,6 +53,7 @@ public class SatellitePosition implements Parcelable {
* Constructor for {@link SatellitePosition} used to create an instance from a {@link Parcel}.
*
* @param in The {@link Parcel} to read the satellite position data from.
+ * @hide
*/
public SatellitePosition(Parcel in) {
mLongitudeDegree = in.readDouble();
@@ -62,12 +65,14 @@ public class SatellitePosition implements Parcelable {
*
* @param longitudeDegree The longitude of the satellite in degrees.
* @param altitudeKm The altitude of the satellite in kilometers.
+ * @hide
*/
public SatellitePosition(double longitudeDegree, double altitudeKm) {
mLongitudeDegree = longitudeDegree;
mAltitudeKm = altitudeKm;
}
+ @NonNull
public static final Creator<SatellitePosition> CREATOR = new Creator<SatellitePosition>() {
@Override
public SatellitePosition createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index e8ae0f56ee9e..6b95eb3c1ac3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -48,9 +48,8 @@ public interface SatelliteProvisionStateCallback {
*
* @param satelliteSubscriberProvisionStatus The List contains the latest provisioning states
* of the SatelliteSubscriberInfos.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
default void onSatelliteSubscriptionProvisionStateChanged(
@NonNull List<SatelliteSubscriberProvisionStatus>
satelliteSubscriberProvisionStatus) {};
diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
index 1c59449b7c73..0cdba83415c2 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
@@ -30,8 +30,7 @@ import java.util.Objects;
*
* @hide
*/
-public class SatelliteSessionStats implements Parcelable {
-
+public final class SatelliteSessionStats implements Parcelable {
private int mCountOfSuccessfulUserMessages;
private int mCountOfUnsuccessfulUserMessages;
private int mCountOfTimedOutUserMessagesWaitingForConnection;
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index dbe5dddf8ce8..8427057fcaab 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -19,6 +19,7 @@ package android.telephony.satellite;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,7 +38,8 @@ import java.util.Objects;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteSubscriberInfo implements Parcelable {
/** provision subscriberId */
@NonNull
@@ -50,10 +52,8 @@ public final class SatelliteSubscriberInfo implements Parcelable {
private int mSubId;
/** SubscriberId format is the ICCID. */
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final int ICCID = 0;
/** SubscriberId format is the 6 digit of IMSI + MSISDN. */
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final int IMSI_MSISDN = 1;
/** Type of subscriber id */
@@ -70,6 +70,9 @@ public final class SatelliteSubscriberInfo implements Parcelable {
readFromParcel(in);
}
+ /**
+ * @hide
+ */
public SatelliteSubscriberInfo(@NonNull Builder builder) {
this.mSubscriberId = builder.mSubscriberId;
this.mCarrierId = builder.mCarrierId;
@@ -80,11 +83,8 @@ public final class SatelliteSubscriberInfo implements Parcelable {
/**
* Builder class for constructing SatelliteSubscriberInfo objects
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static class Builder {
+ public static final class Builder {
@NonNull private String mSubscriberId;
private int mCarrierId;
@NonNull
@@ -95,17 +95,15 @@ public final class SatelliteSubscriberInfo implements Parcelable {
/**
* Set the SubscriberId and returns the Builder class.
- *
- * @hide
*/
- public Builder setSubscriberId(String subscriberId) {
+ @NonNull
+ public Builder setSubscriberId(@NonNull String subscriberId) {
mSubscriberId = subscriberId;
return this;
}
/**
* Set the CarrierId and returns the Builder class.
- * @hide
*/
@NonNull
public Builder setCarrierId(int carrierId) {
@@ -114,18 +112,19 @@ public final class SatelliteSubscriberInfo implements Parcelable {
}
/**
- * Set the niddApn and returns the Builder class.
- * @hide
+ * Set NIDD (Non IP Data) APN can be used for carrier roaming to satellite attachment
+ * and returns the Builder class.
+ *
+ * Refer specification 3GPP TS 23.501 V19.1.0 section 5.31.5
*/
@NonNull
- public Builder setNiddApn(String niddApn) {
+ public Builder setNiddApn(@NonNull String niddApn) {
mNiddApn = niddApn;
return this;
}
/**
* Set the subId and returns the Builder class.
- * @hide
*/
@NonNull
public Builder setSubId(int subId) {
@@ -135,7 +134,6 @@ public final class SatelliteSubscriberInfo implements Parcelable {
/**
* Set the SubscriberIdType and returns the Builder class.
- * @hide
*/
@NonNull
public Builder setSubscriberIdType(@SubscriberIdType int subscriberIdType) {
@@ -145,7 +143,6 @@ public final class SatelliteSubscriberInfo implements Parcelable {
/**
* Returns SatelliteSubscriberInfo object.
- * @hide
*/
@NonNull
public SatelliteSubscriberInfo build() {
@@ -153,11 +150,7 @@ public final class SatelliteSubscriberInfo implements Parcelable {
}
}
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mSubscriberId);
out.writeInt(mCarrierId);
@@ -166,7 +159,7 @@ public final class SatelliteSubscriberInfo implements Parcelable {
out.writeInt(mSubscriberIdType);
}
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+
public static final @android.annotation.NonNull Creator<SatelliteSubscriberInfo> CREATOR =
new Creator<SatelliteSubscriberInfo>() {
@Override
@@ -180,56 +173,45 @@ public final class SatelliteSubscriberInfo implements Parcelable {
}
};
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int describeContents() {
return 0;
}
/**
- * @return provision subscriberId.
- * @hide
+ * Return subscriberId which is used to register with satellite gateway service
+ * during provisioning.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @NonNull
public String getSubscriberId() {
return mSubscriberId;
}
/**
- * @return carrierId.
- * @hide
+ * Return carrierId of the subscription used for provisioning.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int getCarrierId() {
return mCarrierId;
}
/**
- * @return niddApn.
- * @hide
+ * Return the NIDD(Non IP Data) APN which is used for carrier roaming to satellite attachment.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @NonNull
public String getNiddApn() {
return mNiddApn;
}
/**
- * @return subId.
- * @hide
+ * Return the subscriptionId of the subscription which is used for satellite attachment.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int getSubId() {
return mSubId;
}
/**
- * @return subscriberIdType.
- * @hide
+ * Return the type of subscriberId used for provisioning.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public @SubscriberIdType int getSubscriberIdType() {
return mSubscriberIdType;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
index 08ef3f2d8d12..fb4f89ded547 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
@@ -18,6 +18,7 @@ package android.telephony.satellite;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,49 +31,49 @@ import java.util.Objects;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public class SatelliteSubscriberProvisionStatus implements Parcelable {
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+public final class SatelliteSubscriberProvisionStatus implements Parcelable {
private SatelliteSubscriberInfo mSubscriberInfo;
/** {@code true} mean the satellite subscriber is provisioned, {@code false} otherwise. */
- private boolean mProvisionStatus;
+ private boolean mProvisioned;
+ /**
+ * @hide
+ */
public SatelliteSubscriberProvisionStatus(@NonNull Builder builder) {
mSubscriberInfo = builder.mSubscriberInfo;
- mProvisionStatus = builder.mProvisionStatus;
+ mProvisioned = builder.mProvisioned;
}
/**
* Builder class for constructing SatelliteSubscriberProvisionStatus objects
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static class Builder {
+ public static final class Builder {
private SatelliteSubscriberInfo mSubscriberInfo;
- private boolean mProvisionStatus;
+ private boolean mProvisioned;
/**
* Set the SatelliteSubscriberInfo and returns the Builder class.
- * @hide
*/
- public Builder setSatelliteSubscriberInfo(SatelliteSubscriberInfo satelliteSubscriberInfo) {
+ @NonNull
+ public Builder setSatelliteSubscriberInfo(
+ @NonNull SatelliteSubscriberInfo satelliteSubscriberInfo) {
mSubscriberInfo = satelliteSubscriberInfo;
return this;
}
/**
* Set the SatelliteSubscriberInfo's provisionStatus and returns the Builder class.
- * @hide
*/
@NonNull
- public Builder setProvisionStatus(boolean provisionStatus) {
- mProvisionStatus = provisionStatus;
+ public Builder setProvisioned(boolean provisioned) {
+ mProvisioned = provisioned;
return this;
}
/**
* Returns SatelliteSubscriberProvisionStatus object.
- * @hide
*/
@NonNull
public SatelliteSubscriberProvisionStatus build() {
@@ -84,17 +85,12 @@ public class SatelliteSubscriberProvisionStatus implements Parcelable {
readFromParcel(in);
}
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mSubscriberInfo, flags);
- out.writeBoolean(mProvisionStatus);
+ out.writeBoolean(mProvisioned);
}
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final @android.annotation.NonNull Creator<SatelliteSubscriberProvisionStatus>
CREATOR =
new Creator<SatelliteSubscriberProvisionStatus>() {
@@ -109,11 +105,7 @@ public class SatelliteSubscriberProvisionStatus implements Parcelable {
}
};
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int describeContents() {
return 0;
}
@@ -121,9 +113,7 @@ public class SatelliteSubscriberProvisionStatus implements Parcelable {
/**
* SatelliteSubscriberInfo that has a provisioning state.
* @return SatelliteSubscriberInfo.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public @NonNull SatelliteSubscriberInfo getSatelliteSubscriberInfo() {
return mSubscriberInfo;
}
@@ -131,11 +121,9 @@ public class SatelliteSubscriberProvisionStatus implements Parcelable {
/**
* SatelliteSubscriberInfo's provisioning state.
* @return {@code true} means provisioning. {@code false} means deprovisioning.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public @NonNull boolean getProvisionStatus() {
- return mProvisionStatus;
+ public boolean isProvisioned() {
+ return mProvisioned;
}
@NonNull
@@ -148,13 +136,13 @@ public class SatelliteSubscriberProvisionStatus implements Parcelable {
sb.append(",");
sb.append("ProvisionStatus:");
- sb.append(mProvisionStatus);
+ sb.append(mProvisioned);
return sb.toString();
}
@Override
public int hashCode() {
- return Objects.hash(mSubscriberInfo, mProvisionStatus);
+ return Objects.hash(mSubscriberInfo, mProvisioned);
}
@Override
@@ -163,12 +151,12 @@ public class SatelliteSubscriberProvisionStatus implements Parcelable {
if (!(o instanceof SatelliteSubscriberProvisionStatus)) return false;
SatelliteSubscriberProvisionStatus that = (SatelliteSubscriberProvisionStatus) o;
return Objects.equals(mSubscriberInfo, that.mSubscriberInfo)
- && mProvisionStatus == that.mProvisionStatus;
+ && mProvisioned == that.mProvisioned;
}
private void readFromParcel(Parcel in) {
mSubscriberInfo = in.readParcelable(SatelliteSubscriberInfo.class.getClassLoader(),
SatelliteSubscriberInfo.class);
- mProvisionStatus = in.readBoolean();
+ mProvisioned = in.readBoolean();
}
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
index 2ef19f8dd69e..1897a9b7a428 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
@@ -16,10 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -28,6 +32,8 @@ import java.util.Objects;
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteSubscriptionInfo implements Parcelable {
/**
* The ICC ID used for satellite attachment.
@@ -39,6 +45,9 @@ public final class SatelliteSubscriptionInfo implements Parcelable {
*/
@NonNull private final String mNiddApn;
+ /**
+ * @hide
+ */
public SatelliteSubscriptionInfo(@NonNull String iccId, @NonNull String niddApn) {
mIccId = iccId;
mNiddApn = niddApn;
@@ -60,7 +69,8 @@ public final class SatelliteSubscriptionInfo implements Parcelable {
dest.writeString8(mNiddApn);
}
- @NonNull public static final Creator<SatelliteSubscriptionInfo> CREATOR = new Creator<>() {
+ @NonNull
+ public static final Creator<SatelliteSubscriptionInfo> CREATOR = new Creator<>() {
@Override
public SatelliteSubscriptionInfo createFromParcel(Parcel in) {
return new SatelliteSubscriptionInfo(in);
@@ -94,11 +104,17 @@ public final class SatelliteSubscriptionInfo implements Parcelable {
return Objects.hash(getIccId(), getNiddApn());
}
+ /**
+ * Get ICC ID used for satellite attachment.
+ */
@NonNull
public String getIccId() {
return mIccId;
}
+ /**
+ * Get the NIDD(Non IP Data) APN to be used for carrier roaming to satellite attachment.
+ */
@NonNull
public String getNiddApn() {
return mNiddApn;
diff --git a/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java
index 7e19bd13140d..5487eb6ac942 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import com.android.internal.telephony.flags.Flags;
@@ -25,16 +26,14 @@ import com.android.internal.telephony.flags.Flags;
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SatelliteSupportedStateCallback {
/**
* Called when satellite supported state changes.
*
* @param supported The new supported state. {@code true} means satellite is supported,
* {@code false} means satellite is not supported.
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteSupportedStateChanged(boolean supported);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index 046ae5fdeb3c..b5dfb631609c 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -58,12 +58,11 @@ public interface SatelliteTransmissionUpdateCallback {
* @param state The new send datagram transfer state.
* @param sendPendingCount The number of datagrams that are currently being sent.
* @param errorCode If datagram transfer failed, the reason for failure.
- *
- * @hide
*/
- void onSendDatagramStateChanged(@SatelliteManager.DatagramType int datagramType,
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ default void onSendDatagramStateChanged(@SatelliteManager.DatagramType int datagramType,
@SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
- @SatelliteManager.SatelliteResult int errorCode);
+ @SatelliteManager.SatelliteResult int errorCode) {}
/**
* Called when satellite datagram receive state changed.
*
@@ -80,8 +79,7 @@ public interface SatelliteTransmissionUpdateCallback {
* Called when framework receives a request to send a datagram.
*
* @param datagramType The type of the requested datagram.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
default void onSendDatagramRequested(@SatelliteManager.DatagramType int datagramType) {}
}
diff --git a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
index c2ac44f0067e..61e1e5fa8e7e 100644
--- a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
+++ b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
@@ -16,19 +16,26 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.IntArray;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SystemSelectionSpecifier implements Parcelable {
/** Network plmn associated with channel information. */
@@ -188,26 +195,41 @@ public final class SystemSelectionSpecifier implements Parcelable {
return Objects.hash(mMccMnc, mBands, mEarfcns);
}
+ /** Return network plmn associated with channel information. */
@NonNull public String getMccMnc() {
return mMccMnc;
}
- @NonNull public IntArray getBands() {
- return mBands;
+ /**
+ * Return the frequency bands to scan.
+ * Maximum length of the vector is 8.
+ */
+ @NonNull public int[] getBands() {
+ return mBands.toArray();
}
- @NonNull public IntArray getEarfcns() {
- return mEarfcns;
+ /**
+ * Return the radio channels to scan as defined in 3GPP TS 25.101 and 36.101.
+ * Maximum length of the vector is 32.
+ */
+ @NonNull public int[] getEarfcns() {
+ return mEarfcns.toArray();
}
+ /** Return the list of satellites configured for the current location. */
@NonNull
- public SatelliteInfo[] getSatelliteInfos() {
- return mSatelliteInfos;
+ public List<SatelliteInfo> getSatelliteInfos() {
+ return Arrays.stream(mSatelliteInfos).toList();
}
+ /**
+ * Return the list of tag IDs associated with the current location
+ * Tag Ids are generic IDs an OEM can configure. Each tag ID can map to a region which can be
+ * used by OEM to identify proprietary configuration for that region.
+ */
@NonNull
- public IntArray getTagIds() {
- return mTagIds;
+ public int[] getTagIds() {
+ return mTagIds.toArray();
}
private void readFromParcel(Parcel in) {
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 974cc14ae444..71327dcfc3b1 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -83,6 +83,12 @@ interface IPhoneSubInfo {
String getGroupIdLevel1ForSubscriber(int subId, String callingPackage,
String callingFeatureId);
+ /**
+ * Retrieves the Group Identifier Level1 for GSM phones of a subId.
+ */
+ String getGroupIdLevel2ForSubscriber(int subId, String callingPackage,
+ String callingFeatureId);
+
/** @deprecared Use {@link getIccSerialNumberWithFeature(String, String)} instead */
@UnsupportedAppUsage
String getIccSerialNumber(String callingPackage);
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
new file mode 100644
index 000000000000..6573c2c83f20
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.tools.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+class BottomHalfPipAppHelper(
+ instrumentation: Instrumentation,
+ private val useLaunchingActivity: Boolean = false,
+) : PipAppHelper(
+ instrumentation,
+ appName = ActivityOptions.BottomHalfPip.LABEL,
+ componentNameMatcher = ActivityOptions.BottomHalfPip.COMPONENT
+ .toFlickerComponent()
+) {
+ override val openAppIntent: Intent
+ get() = super.openAppIntent.apply {
+ component = if (useLaunchingActivity) {
+ ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT
+ } else {
+ ActivityOptions.BottomHalfPip.COMPONENT
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 9ce8e807f612..7c24a4adca3d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -347,6 +347,27 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".BottomHalfPipLaunchingActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="BottomHalfPipLaunchingActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".BottomHalfPipActivity"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.BottomHalfPipLaunchingActivity"
+ android:theme="@style/TranslucentTheme"
+ android:label="BottomHalfPipActivity"
+ android:exported="true">
+ </activity>
<activity android:name=".SplitScreenActivity"
android:resizeableActivity="true"
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 47d113717ae0..837d050b73ff 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -62,6 +62,12 @@
<item name="android:backgroundDimEnabled">false</item>
</style>
+ <style name="TranslucentTheme" parent="@style/OptOutEdgeToEdge">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
+
<style name="no_starting_window" parent="@style/OptOutEdgeToEdge">
<item name="android:windowDisablePreview">true</item>
</style>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 73625da9dfa5..0c1ac9951d32 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -241,6 +241,21 @@ public class ActivityOptions {
FLICKER_APP_PACKAGE + ".PipActivity");
}
+ public static class BottomHalfPip {
+ public static final String LAUNCHING_APP_LABEL = "BottomHalfPipLaunchingActivity";
+ // Test App > Bottom Half PIP Activity
+ public static final String LABEL = "BottomHalfPipActivity";
+
+ // Use the bottom half layout for PIP Activity
+ public static final String EXTRA_BOTTOM_HALF_LAYOUT = "bottom_half";
+
+ public static final ComponentName LAUNCHING_APP_COMPONENT = new ComponentName(
+ FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".BottomHalfPipLaunchingActivity");
+
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".BottomHalfPipActivity");
+ }
+
public static class SplitScreen {
public static class Primary {
public static final String LABEL = "SplitScreenPrimaryActivity";
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
new file mode 100644
index 000000000000..3d4865572486
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+public class BottomHalfPipActivity extends PipActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTheme(R.style.TranslucentTheme);
+ updateLayout();
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ updateLayout();
+ }
+
+ /**
+ * Sets to match parent layout if the activity is
+ * {@link Activity#isInPictureInPictureMode()}. Otherwise, set to bottom half
+ * layout.
+ *
+ * @see #setToBottomHalfMode(boolean)
+ */
+ private void updateLayout() {
+ setToBottomHalfMode(!isInPictureInPictureMode());
+ }
+
+ /**
+ * Sets `useBottomHalfLayout` to `true` to use the bottom half layout. Use the
+ * [LayoutParams.MATCH_PARENT] layout.
+ */
+ private void setToBottomHalfMode(boolean useBottomHalfLayout) {
+ final WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ if (useBottomHalfLayout) {
+ final int taskHeight = getWindowManager().getCurrentWindowMetrics().getBounds()
+ .height();
+ attrs.y = taskHeight / 2;
+ attrs.height = taskHeight / 2;
+ } else {
+ attrs.y = 0;
+ attrs.height = LayoutParams.MATCH_PARENT;
+ }
+ getWindow().setAttributes(attrs);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
new file mode 100644
index 000000000000..d9d4361411bb
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class BottomHalfPipLaunchingActivity extends SimpleActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = new Intent(this, BottomHalfPipActivity.class);
+ startActivity(intent);
+ }
+}