summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java17
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserSwitchWaiter.java80
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java41
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java7
-rw-r--r--api/OWNERS4
-rw-r--r--cmds/bootanimation/BootAnimation.cpp4
-rw-r--r--config/preloaded-classes-denylist1
-rw-r--r--core/api/current.txt56
-rw-r--r--core/api/system-current.txt1
-rw-r--r--core/java/android/app/Activity.java3
-rw-r--r--core/java/android/app/ActivityThread.java11
-rw-r--r--core/java/android/app/AppOpsManager.java1
-rw-r--r--core/java/android/app/ClientTransactionHandler.java4
-rw-r--r--core/java/android/app/NotificationChannelGroup.java21
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java19
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java3
-rw-r--r--core/java/android/hardware/face/FaceManager.java40
-rw-r--r--core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl35
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl9
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java28
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java11
-rw-r--r--core/java/android/os/BatteryStats.java7
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/UserManager.java27
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java15
-rw-r--r--core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java1
-rw-r--r--core/java/android/util/FeatureFlagUtils.java5
-rw-r--r--core/java/android/view/SurfaceControl.java21
-rw-r--r--core/java/android/view/SurfaceView.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java27
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java37
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java72
-rw-r--r--core/java/android/view/accessibility/DirectAccessibilityConnection.java136
-rw-r--r--core/java/android/view/inputmethod/DeleteGesture.aidl (renamed from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiModel.kt)14
-rw-r--r--core/java/android/view/inputmethod/DeleteGesture.java193
-rw-r--r--core/java/android/view/inputmethod/HandwritingGesture.java94
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java14
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java15
-rw-r--r--core/java/android/view/inputmethod/InsertGesture.aidl19
-rw-r--r--core/java/android/view/inputmethod/InsertGesture.java172
-rw-r--r--core/java/android/view/inputmethod/SelectGesture.aidl19
-rw-r--r--core/java/android/view/inputmethod/SelectGesture.java191
-rw-r--r--core/java/android/widget/ExpandableListView.java19
-rw-r--r--core/java/android/window/ITaskFragmentOrganizerController.aidl15
-rw-r--r--core/java/android/window/TaskFragmentOrganizer.java32
-rw-r--r--core/java/android/window/TaskFragmentTransaction.java19
-rw-r--r--core/java/com/android/internal/app/SuggestedLocaleAdapter.java6
-rw-r--r--core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl13
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java66
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java21
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java1316
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java8
-rw-r--r--core/java/com/android/internal/util/FastDataOutput.java6
-rw-r--r--core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java1
-rw-r--r--core/jni/OWNERS2
-rw-r--r--core/jni/android/opengl/util.cpp51
-rw-r--r--core/jni/android_view_SurfaceControl.cpp28
-rw-r--r--core/res/res/layout/floating_popup_container.xml1
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java6
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java3
-rw-r--r--core/tests/coretests/src/android/widget/FloatingToolbarUtils.java23
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java4
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--data/fonts/fonts.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu.xml153
-rw-r--r--libs/WindowManager/Shell/res/values/colors_tv.xml1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java234
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java290
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java212
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java2
-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.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/UserChangeListener.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt)28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt143
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt177
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java79
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java83
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java111
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java76
-rw-r--r--libs/hwui/hwui/Typeface.cpp9
-rw-r--r--libs/hwui/hwui/Typeface.h3
-rw-r--r--libs/hwui/jni/Typeface.cpp25
-rw-r--r--libs/hwui/renderthread/VulkanManager.h2
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp67
-rw-r--r--media/java/android/media/MediaRouter2Manager.java53
-rw-r--r--native/android/system_fonts.cpp41
-rw-r--r--packages/SettingsLib/Spa/.idea/codeStyles/Project.xml9
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt11
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt11
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/HomePage.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PageRepository.kt10
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt15
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt17
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt15
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt15
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LogCompositions.kt39
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Pager.kt339
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PagerState.kt318
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt7
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt33
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt58
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt46
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt57
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt27
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt73
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTab.kt31
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt18
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt5
-rw-r--r--packages/SettingsLib/SpaPrivileged/Android.bp5
-rw-r--r--packages/SettingsLib/SpaPrivileged/res/values/strings.xml20
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt26
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt125
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppOpsController.kt)2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRecord.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRecord.kt)2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt)8
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/Apps.kt)2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt114
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt)2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/enterprise/EnterpriseRepository.kt)2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt12
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt111
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt64
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/WorkProfilePager.kt)4
-rw-r--r--packages/SettingsLib/res/values/strings.xml15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java137
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/OWNERS2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java47
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values-land/dimens.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values-sw360dp-land/dimens.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/layout/new_status_bar_wifi_group.xml28
-rw-r--r--packages/SystemUI/res/layout/status_bar_wifi_group.xml63
-rw-r--r--packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml82
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt37
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java190
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepo.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java152
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt132
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepoTest.kt252
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt396
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java6
-rw-r--r--services/OWNERS2
-rw-r--r--services/api/OWNERS2
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java17
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java14
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java392
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceInterface.java99
-rw-r--r--services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java379
-rw-r--r--services/core/java/com/android/server/appop/OnOpModeChangedListener.java102
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java4
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java61
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java243
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java94
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java221
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java71
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java373
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java72
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java37
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java16
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java2
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java55
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java43
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java15
-rw-r--r--services/core/java/com/android/server/pm/Computer.java9
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java29
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerInternalBase.java3
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java96
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java23
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java80
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java11
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java10
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java2
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java1796
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java12
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java24
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java30
-rw-r--r--services/core/java/com/android/server/wm/AppWarnings.java53
-rw-r--r--services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java27
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java6
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java11
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java104
-rw-r--r--services/core/java/com/android/server/wm/Transition.java39
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java6
-rw-r--r--services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java20
-rw-r--r--services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/jni/OWNERS2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java15
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java53
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java27
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java106
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java172
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java51
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java168
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java199
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java9
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java30
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java82
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt40
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt26
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt18
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt6
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp16
-rw-r--r--tools/aapt2/integration-tests/DumpTest/components.apkbin84534 -> 92659 bytes
-rw-r--r--tools/aapt2/integration-tests/DumpTest/components_full_proto.txt4
-rw-r--r--tools/lint/Android.bp6
-rw-r--r--tools/lint/README.md4
-rw-r--r--tools/lint/fix/README.md46
-rw-r--r--tools/lint/fix/lint_fix.py76
425 files changed, 11942 insertions, 5614 deletions
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 448ee6160ce0..d5c386fc5368 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -155,6 +155,7 @@ public class UserLifecycleTests {
public void tearDown() throws Exception {
setSystemProperty("debug.usercontroller.user_switch_timeout_ms", mUserSwitchTimeoutMs);
mBroadcastWaiter.close();
+ mUserSwitchWaiter.close();
for (int userId : mUsersToRemove) {
try {
mUm.removeUser(userId);
@@ -207,10 +208,10 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
- mRunner.resumeTiming();
- Log.i(TAG, "Starting timer");
-
runThenWaitForBroadcasts(userId, () -> {
+ mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
+
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED);
@@ -273,9 +274,7 @@ public class UserLifecycleTests {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
- runThenWaitForBroadcasts(testUser, () -> {
- mAm.switchUser(testUser);
- }, Intent.ACTION_USER_UNLOCKED);
+ switchUser(testUser);
mRunner.pauseTiming();
Log.i(TAG, "Stopping timer");
@@ -362,10 +361,10 @@ public class UserLifecycleTests {
}, Intent.ACTION_MEDIA_MOUNTED);
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
- mRunner.resumeTiming();
- Log.i(TAG, "Starting timer");
-
runThenWaitForBroadcasts(userId, () -> {
+ mRunner.resumeTiming();
+ Log.i(TAG, "Starting timer");
+
mAm.switchUser(startUser);
}, Intent.ACTION_USER_STOPPED);
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserSwitchWaiter.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserSwitchWaiter.java
index 228d14c3a05b..82245977760b 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserSwitchWaiter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserSwitchWaiter.java
@@ -17,61 +17,87 @@
package android.multiuser;
import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.IUserSwitchObserver;
import android.app.UserSwitchObserver;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.util.FunctionalUtils;
-import java.util.concurrent.CountDownLatch;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-public class UserSwitchWaiter {
+public class UserSwitchWaiter implements Closeable {
private final String mTag;
private final int mTimeoutInSecond;
+ private final IActivityManager mActivityManager;
+ private final IUserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ getSemaphoreSwitchComplete(newUserId).release();
+ }
+
+ @Override
+ public void onLockedBootComplete(int newUserId) {
+ getSemaphoreBootComplete(newUserId).release();
+ }
+ };
+
+ private final Map<Integer, Semaphore> mSemaphoresMapSwitchComplete = new ConcurrentHashMap<>();
+ private Semaphore getSemaphoreSwitchComplete(final int userId) {
+ return mSemaphoresMapSwitchComplete.computeIfAbsent(userId,
+ (Integer absentKey) -> new Semaphore(0));
+ }
+
+ private final Map<Integer, Semaphore> mSemaphoresMapBootComplete = new ConcurrentHashMap<>();
+ private Semaphore getSemaphoreBootComplete(final int userId) {
+ return mSemaphoresMapBootComplete.computeIfAbsent(userId,
+ (Integer absentKey) -> new Semaphore(0));
+ }
- public UserSwitchWaiter(String tag, int timeoutInSecond) {
+ public UserSwitchWaiter(String tag, int timeoutInSecond) throws RemoteException {
mTag = tag;
mTimeoutInSecond = timeoutInSecond;
+ mActivityManager = ActivityManager.getService();
+
+ mActivityManager.registerUserSwitchObserver(mUserSwitchObserver, mTag);
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ mActivityManager.unregisterUserSwitchObserver(mUserSwitchObserver);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Failed to unregister user switch observer", e);
+ }
}
public void runThenWaitUntilSwitchCompleted(int userId,
FunctionalUtils.ThrowingRunnable runnable, Runnable onFail) throws RemoteException {
- final CountDownLatch latch = new CountDownLatch(1);
- ActivityManager.getService().registerUserSwitchObserver(
- new UserSwitchObserver() {
- @Override
- public void onUserSwitchComplete(int newUserId) throws RemoteException {
- if (userId == newUserId) {
- latch.countDown();
- }
- }
- }, mTag);
+ final Semaphore semaphore = getSemaphoreSwitchComplete(userId);
+ semaphore.drainPermits();
runnable.run();
- waitForLatch(latch, onFail);
+ waitForSemaphore(semaphore, onFail);
}
public void runThenWaitUntilBootCompleted(int userId,
FunctionalUtils.ThrowingRunnable runnable, Runnable onFail) throws RemoteException {
- final CountDownLatch latch = new CountDownLatch(1);
- ActivityManager.getService().registerUserSwitchObserver(
- new UserSwitchObserver() {
- @Override
- public void onLockedBootComplete(int newUserId) {
- if (userId == newUserId) {
- latch.countDown();
- }
- }
- }, mTag);
+ final Semaphore semaphore = getSemaphoreBootComplete(userId);
+ semaphore.drainPermits();
runnable.run();
- waitForLatch(latch, onFail);
+ waitForSemaphore(semaphore, onFail);
}
- private void waitForLatch(CountDownLatch latch, Runnable onFail) {
+ private void waitForSemaphore(Semaphore semaphore, Runnable onFail) {
boolean success = false;
try {
- success = latch.await(mTimeoutInSecond, TimeUnit.SECONDS);
+ success = semaphore.tryAcquire(mTimeoutInSecond, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(mTag, "Thread interrupted unexpectedly.", e);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 59cd82e971bf..3bbc5a369684 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -176,12 +176,13 @@ public final class BatteryController extends RestrictingController {
Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+ powerConnected + "/" + stablePower + "/" + batteryNotLow);
}
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+
mFlexibilityController.setConstraintSatisfied(
- JobStatus.CONSTRAINT_CHARGING, mService.isBatteryCharging());
- mFlexibilityController
- .setConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow);
+ JobStatus.CONSTRAINT_CHARGING, mService.isBatteryCharging(), nowElapsed);
+ mFlexibilityController.setConstraintSatisfied(
+ JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
- final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
if (ts.hasChargingConstraint()) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 2e41dfd2888c..3ca1ad58955d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -137,9 +137,9 @@ public final class FlexibilityController extends StateController {
new PrefetchController.PrefetchChangedListener() {
@Override
public void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs, int userId,
- String pkgName, long prevEstimatedLaunchTime, long newEstimatedLaunchTime) {
+ String pkgName, long prevEstimatedLaunchTime,
+ long newEstimatedLaunchTime, long nowElapsed) {
synchronized (mLock) {
- final long nowElapsed = sElapsedRealtimeClock.millis();
final long prefetchThreshold =
mPrefetchController.getLaunchTimeThresholdMs();
boolean jobWasInPrefetchWindow = prevEstimatedLaunchTime
@@ -158,8 +158,8 @@ public final class FlexibilityController extends StateController {
if (!js.hasFlexibilityConstraint()) {
continue;
}
- mFlexibilityTracker.resetJobNumDroppedConstraints(js);
- mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
}
}
@@ -191,7 +191,7 @@ public final class FlexibilityController extends StateController {
js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
final long nowElapsed = sElapsedRealtimeClock.millis();
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
- mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
}
}
@@ -239,7 +239,7 @@ public final class FlexibilityController extends StateController {
* Changes flexibility constraint satisfaction for affected jobs.
*/
@VisibleForTesting
- void setConstraintSatisfied(int constraint, boolean state) {
+ void setConstraintSatisfied(int constraint, boolean state, long nowElapsed) {
synchronized (mLock) {
final boolean old = (mSatisfiedFlexibleConstraints & constraint) != 0;
if (old == state) {
@@ -255,8 +255,6 @@ public final class FlexibilityController extends StateController {
// The rest did not have a change in state and are still satisfied or unsatisfied.
final int numConstraintsToUpdate = Math.max(curSatisfied, prevSatisfied);
- final long nowElapsed = sElapsedRealtimeClock.millis();
-
// In order to get the range of all potentially satisfied jobs, we start at the number
// of satisfied system-wide constraints and iterate to the max number of potentially
// satisfied constraints, determined by how many job-specific constraints exist.
@@ -329,10 +327,9 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
@GuardedBy("mLock")
- int getCurPercentOfLifecycleLocked(JobStatus js) {
+ int getCurPercentOfLifecycleLocked(JobStatus js, long nowElapsed) {
final long earliest = getLifeCycleBeginningElapsedLocked(js);
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
- final long nowElapsed = sElapsedRealtimeClock.millis();
if (latest == NO_LIFECYCLE_END || earliest >= nowElapsed) {
return 0;
}
@@ -414,8 +411,8 @@ public final class FlexibilityController extends StateController {
.getJobsByNumRequiredConstraints(j);
for (int i = 0; i < jobs.size(); i++) {
JobStatus js = jobs.valueAt(i);
- mFlexibilityTracker.resetJobNumDroppedConstraints(js);
- mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
if (js.setFlexibilityConstraintSatisfied(
nowElapsed, isFlexibilitySatisfiedLocked(js))) {
changedJobs.add(js);
@@ -479,8 +476,8 @@ public final class FlexibilityController extends StateController {
mTrackedJobs.get(js.getNumRequiredFlexibleConstraints() - 1).remove(js);
}
- public void resetJobNumDroppedConstraints(JobStatus js) {
- final int curPercent = getCurPercentOfLifecycleLocked(js);
+ public void resetJobNumDroppedConstraints(JobStatus js, long nowElapsed) {
+ final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
int toDrop = 0;
final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ (js.getPreferUnmetered() ? 1 : 0);
@@ -489,7 +486,8 @@ public final class FlexibilityController extends StateController {
toDrop++;
}
}
- adjustJobsRequiredConstraints(js, js.getNumDroppedFlexibleConstraints() - toDrop);
+ adjustJobsRequiredConstraints(
+ js, js.getNumDroppedFlexibleConstraints() - toDrop, nowElapsed);
}
/** Returns all tracked jobs. */
@@ -502,13 +500,12 @@ public final class FlexibilityController extends StateController {
* Returns false if the job status's number of flexible constraints is now 0.
* Jobs with 0 required flexible constraints are removed from the tracker.
*/
- public boolean adjustJobsRequiredConstraints(JobStatus js, int n) {
+ public boolean adjustJobsRequiredConstraints(JobStatus js, int n, long nowElapsed) {
if (n == 0) {
return false;
}
remove(js);
js.adjustNumRequiredFlexibleConstraints(n);
- final long nowElapsed = sElapsedRealtimeClock.millis();
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
if (js.getNumRequiredFlexibleConstraints() <= 0) {
maybeStopTrackingJobLocked(js, null, false);
@@ -553,7 +550,7 @@ public final class FlexibilityController extends StateController {
return js.getSourceUserId() == userId;
}
- public void scheduleDropNumConstraintsAlarm(JobStatus js) {
+ public void scheduleDropNumConstraintsAlarm(JobStatus js, long nowElapsed) {
long nextTimeElapsed;
synchronized (mLock) {
final long earliest = getLifeCycleBeginningElapsedLocked(js);
@@ -567,7 +564,7 @@ public final class FlexibilityController extends StateController {
if (latest - nextTimeElapsed < mDeadlineProximityLimitMs) {
mFlexibilityTracker.adjustJobsRequiredConstraints(
- js, -js.getNumRequiredFlexibleConstraints());
+ js, -js.getNumRequiredFlexibleConstraints(), nowElapsed);
return;
}
addAlarm(js, nextTimeElapsed);
@@ -578,21 +575,21 @@ public final class FlexibilityController extends StateController {
protected void processExpiredAlarms(@NonNull ArraySet<JobStatus> expired) {
synchronized (mLock) {
ArraySet<JobStatus> changedJobs = new ArraySet<>();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = 0; i < expired.size(); i++) {
JobStatus js = expired.valueAt(i);
boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
final long earliest = getLifeCycleBeginningElapsedLocked(js);
final long latest = getLifeCycleEndElapsedLocked(js, earliest);
- final long nowElapsed = sElapsedRealtimeClock.millis();
if (latest - nowElapsed < mDeadlineProximityLimitMs) {
mFlexibilityTracker.adjustJobsRequiredConstraints(js,
- -js.getNumRequiredFlexibleConstraints());
+ -js.getNumRequiredFlexibleConstraints(), nowElapsed);
} else {
long nextTimeElapsed =
getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1)
+ if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)
&& nextTimeElapsed != NO_LIFECYCLE_END) {
mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index d5750f8d7974..dd0621728724 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -95,9 +95,10 @@ public final class IdleController extends RestrictingController implements Idlen
*/
@Override
public void reportNewIdleState(boolean isIdle) {
- mFlexibilityController.setConstraintSatisfied(JobStatus.CONSTRAINT_IDLE, isIdle);
synchronized (mLock) {
final long nowElapsed = sElapsedRealtimeClock.millis();
+ mFlexibilityController.setConstraintSatisfied(
+ JobStatus.CONSTRAINT_IDLE, isIdle, nowElapsed);
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 0945b7e796fd..e04cec30d26b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -105,7 +105,7 @@ public class PrefetchController extends StateController {
public interface PrefetchChangedListener {
/** Callback to inform listeners when estimated launch times change. */
void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs, int userId, String pkgName,
- long prevEstimatedLaunchTime, long newEstimatedLaunchTime);
+ long prevEstimatedLaunchTime, long newEstimatedLaunchTime, long nowElapsed);
}
@SuppressWarnings("FieldCanBeLocal")
@@ -308,8 +308,9 @@ public class PrefetchController extends StateController {
final long nowElapsed = sElapsedRealtimeClock.millis();
updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed);
for (int i = 0; i < mPrefetchChangedListeners.size(); i++) {
- mPrefetchChangedListeners.valueAt(i).onPrefetchCacheUpdated(jobs,
- userId, pkgName, prevEstimatedLaunchTime, newEstimatedLaunchTime);
+ mPrefetchChangedListeners.valueAt(i).onPrefetchCacheUpdated(
+ jobs, userId, pkgName, prevEstimatedLaunchTime,
+ newEstimatedLaunchTime, nowElapsed);
}
if (maybeUpdateConstraintForPkgLocked(now, nowElapsed, userId, pkgName)) {
mStateChangedListener.onControllerStateChanged(jobs);
diff --git a/api/OWNERS b/api/OWNERS
index 4d8ed0347f43..bf6216c168e8 100644
--- a/api/OWNERS
+++ b/api/OWNERS
@@ -3,7 +3,7 @@ hansson@google.com
# Modularization team
file:platform/packages/modules/common:/OWNERS
-per-file Android.bp = file:platform/build/soong:/OWNERS
+per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
# For metalava team to disable lint checks in platform
-per-file Android.bp = aurimas@google.com,emberrose@google.com,sjgilbert@google.com \ No newline at end of file
+per-file Android.bp = aurimas@google.com,emberrose@google.com,sjgilbert@google.com
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index dae4570456cd..814800bcd9e5 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -615,10 +615,6 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
mWidth = limitedSize.width;
mHeight = limitedSize.height;
- SurfaceComposerClient::Transaction t;
- t.setSize(mFlingerSurfaceControl, mWidth, mHeight);
- t.apply();
-
EGLConfig config = getEglConfig(mDisplay);
EGLSurface surface = eglCreateWindowSurface(mDisplay, config, mFlingerSurface.get(), nullptr);
if (eglMakeCurrent(mDisplay, surface, surface, mContext) == EGL_FALSE) {
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 02f2df6167a5..502d8c6dadb1 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -9,3 +9,4 @@ android.net.rtp.AudioGroup
android.net.rtp.AudioStream
android.net.rtp.RtpStream
java.util.concurrent.ThreadLocalRandom
+com.android.internal.jank.InteractionJankMonitor$InstanceHolder
diff --git a/core/api/current.txt b/core/api/current.txt
index 3efe2ef5baea..795c43056d04 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -51979,6 +51979,7 @@ package android.view.accessibility {
method public boolean isTextEntryKey();
method public boolean isTextSelectable();
method public boolean isVisibleToUser();
+ method public void makeQueryableFromAppProcess(@NonNull android.view.View);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain();
@@ -52985,6 +52986,22 @@ package android.view.inputmethod {
method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
}
+ public final class DeleteGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.graphics.RectF getDeletionArea();
+ method public int getGranularity();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.DeleteGesture> CREATOR;
+ }
+
+ public static final class DeleteGesture.Builder {
+ ctor public DeleteGesture.Builder();
+ method @NonNull public android.view.inputmethod.DeleteGesture build();
+ method @NonNull public android.view.inputmethod.DeleteGesture.Builder setDeletionArea(@NonNull android.graphics.RectF);
+ method @NonNull public android.view.inputmethod.DeleteGesture.Builder setFallbackText(@Nullable String);
+ method @NonNull public android.view.inputmethod.DeleteGesture.Builder setGranularity(int);
+ }
+
public final class EditorBoundsInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.graphics.RectF getEditorBounds();
@@ -53079,6 +53096,12 @@ package android.view.inputmethod {
field public int token;
}
+ public abstract class HandwritingGesture {
+ method @Nullable public String getFallbackText();
+ field public static final int GRANULARITY_CHARACTER = 2; // 0x2
+ field public static final int GRANULARITY_WORD = 1; // 0x1
+ }
+
public final class InlineSuggestion implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.inputmethod.InlineSuggestionInfo getInfo();
@@ -53169,6 +53192,7 @@ package android.view.inputmethod {
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
+ method public default void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public default boolean performSpellCheck();
method public boolean reportFullscreenMode(boolean);
@@ -53390,6 +53414,38 @@ package android.view.inputmethod {
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
+ public final class InsertGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.graphics.PointF getInsertionPoint();
+ method @Nullable public String getTextToInsert();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InsertGesture> CREATOR;
+ }
+
+ public static final class InsertGesture.Builder {
+ ctor public InsertGesture.Builder();
+ method @NonNull public android.view.inputmethod.InsertGesture build();
+ method @NonNull public android.view.inputmethod.InsertGesture.Builder setFallbackText(@Nullable String);
+ method @NonNull public android.view.inputmethod.InsertGesture.Builder setInsertionPoint(@NonNull android.graphics.PointF);
+ method @NonNull public android.view.inputmethod.InsertGesture.Builder setTextToInsert(@NonNull String);
+ }
+
+ public final class SelectGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getGranularity();
+ method @NonNull public android.graphics.RectF getSelectionArea();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SelectGesture> CREATOR;
+ }
+
+ public static final class SelectGesture.Builder {
+ ctor public SelectGesture.Builder();
+ method @NonNull public android.view.inputmethod.SelectGesture build();
+ method @NonNull public android.view.inputmethod.SelectGesture.Builder setFallbackText(@Nullable String);
+ method @NonNull public android.view.inputmethod.SelectGesture.Builder setGranularity(int);
+ method @NonNull public android.view.inputmethod.SelectGesture.Builder setSelectionArea(@NonNull android.graphics.RectF);
+ }
+
public final class SurroundingText implements android.os.Parcelable {
ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
method public int describeContents();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index be84032df9a4..a0cc4f0d6775 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11479,6 +11479,7 @@ package android.service.notification {
method public void onPanelRevealed(int);
method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
method public final void unsnoozeNotification(@NonNull String);
+ field public static final String ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS = "android.service.notification.action.NOTIFICATION_ASSISTANT_DETAIL_SETTINGS";
field public static final String FEEDBACK_RATING = "feedback.rating";
field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
field public static final int SOURCE_FROM_APP = 0; // 0x0
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 25ef6e833565..c2b315f7088d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -980,7 +980,8 @@ public class Activity extends ContextThemeWrapper
boolean mEnterAnimationComplete;
private boolean mIsInMultiWindowMode;
- private boolean mIsInPictureInPictureMode;
+ /** @hide */
+ boolean mIsInPictureInPictureMode;
private boolean mShouldDockBigOverlays;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b383d7daafd0..db7681640c67 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4177,7 +4177,8 @@ public final class ActivityThread extends ClientTransactionHandler
private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.activity.isFinishing(),
- /* userLeaving */ true, r.activity.mConfigChangeFlags, /* dontReport */ false));
+ /* userLeaving */ true, r.activity.mConfigChangeFlags, /* dontReport */ false,
+ /* autoEnteringPip */ false));
executeTransaction(transaction);
}
@@ -4965,12 +4966,18 @@ public final class ActivityThread extends ClientTransactionHandler
@Override
public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
- int configChanges, PendingTransactionActions pendingActions, String reason) {
+ int configChanges, boolean autoEnteringPip, PendingTransactionActions pendingActions,
+ String reason) {
if (userLeaving) {
performUserLeavingActivity(r);
}
r.activity.mConfigChangeFlags |= configChanges;
+ if (autoEnteringPip) {
+ // Set mIsInPictureInPictureMode earlier in case of auto-enter-pip, see also
+ // {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
+ r.activity.mIsInPictureInPictureMode = true;
+ }
performPauseActivity(r, finished, reason, pendingActions);
// Make sure any pending writes are now committed.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2a5916dfb6a8..d6b90a2792a9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1906,6 +1906,7 @@ public class AppOpsManager {
OP_SCHEDULE_EXACT_ALARM,
OP_MANAGE_MEDIA,
OP_TURN_SCREEN_ON,
+ OP_GET_USAGE_STATS,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 389da2d094d5..f322ca9654ed 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -96,8 +96,8 @@ public abstract class ClientTransactionHandler {
/** Pause the activity. */
public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished,
- boolean userLeaving, int configChanges, PendingTransactionActions pendingActions,
- String reason);
+ boolean userLeaving, int configChanges, boolean autoEnteringPip,
+ PendingTransactionActions pendingActions, String reason);
/**
* Resume the activity.
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 807bd5764cba..2b245aae915f 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -20,7 +20,6 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
-import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -68,7 +67,7 @@ public final class NotificationChannelGroup implements Parcelable {
private CharSequence mName;
private String mDescription;
private boolean mBlocked;
- private ParceledListSlice<NotificationChannel> mChannels;
+ private List<NotificationChannel> mChannels = new ArrayList<>();
// Bitwise representation of fields that have been changed by the user
private int mUserLockedFields;
@@ -103,8 +102,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
mDescription = null;
}
- mChannels = in.readParcelable(
- NotificationChannelGroup.class.getClassLoader(), ParceledListSlice.class);
+ in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
mBlocked = in.readBoolean();
mUserLockedFields = in.readInt();
}
@@ -131,7 +129,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
dest.writeByte((byte) 0);
}
- dest.writeParcelable(mChannels, flags);
+ dest.writeParcelableList(mChannels, flags);
dest.writeBoolean(mBlocked);
dest.writeInt(mUserLockedFields);
}
@@ -161,7 +159,7 @@ public final class NotificationChannelGroup implements Parcelable {
* Returns the list of channels that belong to this group
*/
public List<NotificationChannel> getChannels() {
- return mChannels == null ? new ArrayList<>() : mChannels.getList();
+ return mChannels;
}
/**
@@ -195,8 +193,15 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
+ public void addChannel(NotificationChannel channel) {
+ mChannels.add(channel);
+ }
+
+ /**
+ * @hide
+ */
public void setChannels(List<NotificationChannel> channels) {
- mChannels = new ParceledListSlice<>(channels);
+ mChannels = channels;
}
/**
@@ -331,7 +336,7 @@ public final class NotificationChannelGroup implements Parcelable {
proto.write(NotificationChannelGroupProto.NAME, mName.toString());
proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
- for (NotificationChannel channel : mChannels.getList()) {
+ for (NotificationChannel channel : mChannels) {
channel.dumpDebug(proto, NotificationChannelGroupProto.CHANNELS);
}
proto.end(token);
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 813e0f93a1f7..965e761ebfb3 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -39,13 +39,14 @@ public class PauseActivityItem extends ActivityLifecycleItem {
private boolean mUserLeaving;
private int mConfigChanges;
private boolean mDontReport;
+ private boolean mAutoEnteringPip;
@Override
public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, pendingActions,
- "PAUSE_ACTIVITY_ITEM");
+ client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, mAutoEnteringPip,
+ pendingActions, "PAUSE_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -71,7 +72,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
/** Obtain an instance initialized with provided params. */
public static PauseActivityItem obtain(boolean finished, boolean userLeaving, int configChanges,
- boolean dontReport) {
+ boolean dontReport, boolean autoEnteringPip) {
PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class);
if (instance == null) {
instance = new PauseActivityItem();
@@ -80,6 +81,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
instance.mUserLeaving = userLeaving;
instance.mConfigChanges = configChanges;
instance.mDontReport = dontReport;
+ instance.mAutoEnteringPip = autoEnteringPip;
return instance;
}
@@ -94,6 +96,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
instance.mUserLeaving = false;
instance.mConfigChanges = 0;
instance.mDontReport = true;
+ instance.mAutoEnteringPip = false;
return instance;
}
@@ -105,6 +108,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
mUserLeaving = false;
mConfigChanges = 0;
mDontReport = false;
+ mAutoEnteringPip = false;
ObjectPool.recycle(this);
}
@@ -117,6 +121,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
dest.writeBoolean(mUserLeaving);
dest.writeInt(mConfigChanges);
dest.writeBoolean(mDontReport);
+ dest.writeBoolean(mAutoEnteringPip);
}
/** Read from Parcel. */
@@ -125,6 +130,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
mUserLeaving = in.readBoolean();
mConfigChanges = in.readInt();
mDontReport = in.readBoolean();
+ mAutoEnteringPip = in.readBoolean();
}
public static final @NonNull Creator<PauseActivityItem> CREATOR =
@@ -148,7 +154,8 @@ public class PauseActivityItem extends ActivityLifecycleItem {
}
final PauseActivityItem other = (PauseActivityItem) o;
return mFinished == other.mFinished && mUserLeaving == other.mUserLeaving
- && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport;
+ && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport
+ && mAutoEnteringPip == other.mAutoEnteringPip;
}
@Override
@@ -158,12 +165,14 @@ public class PauseActivityItem extends ActivityLifecycleItem {
result = 31 * result + (mUserLeaving ? 1 : 0);
result = 31 * result + mConfigChanges;
result = 31 * result + (mDontReport ? 1 : 0);
+ result = 31 * result + (mAutoEnteringPip ? 1 : 0);
return result;
}
@Override
public String toString() {
return "PauseActivityItem{finished=" + mFinished + ",userLeaving=" + mUserLeaving
- + ",configChanges=" + mConfigChanges + ",dontReport=" + mDontReport + "}";
+ + ",configChanges=" + mConfigChanges + ",dontReport=" + mDontReport
+ + ",autoEnteringPip=" + mAutoEnteringPip + "}";
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 25ff8a78a0c8..de1d38a64163 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -227,7 +227,8 @@ public class TransactionExecutor {
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */, mPendingActions,
+ false /* userLeaving */, 0 /* configChanges */,
+ false /* autoEnteringPip */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 7092e43596ec..7247ef77afb4 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -29,6 +29,7 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.CryptoObject;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
@@ -674,6 +675,45 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
+ * Forwards BiometricStateListener to FaceService.
+ *
+ * @param listener new BiometricStateListener being added
+ * @hide
+ */
+ public void registerBiometricStateListener(@NonNull BiometricStateListener listener) {
+ try {
+ mService.registerBiometricStateListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds a callback that gets called when the service registers all of the face
+ * authenticators (HALs).
+ *
+ * If the face authenticators are already registered when the callback is added, the
+ * callback is invoked immediately.
+ *
+ * The callback is automatically removed after it's invoked.
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void addAuthenticatorsRegisteredCallback(
+ IFaceAuthenticatorsRegisteredCallback callback) {
+ if (mService != null) {
+ try {
+ mService.addAuthenticatorsRegisteredCallback(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "addAuthenticatorsRegisteredCallback(): Service not connected!");
+ }
+ }
+
+ /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl
new file mode 100644
index 000000000000..78f978d21ed7
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.face;
+
+import android.hardware.face.FaceSensorPropertiesInternal;
+import java.util.List;
+
+/**
+ * Callback to notify FaceManager that FaceService has registered all of the
+ * face authenticators (HALs).
+ * See {@link android.hardware.face.IFaceService#registerAuthenticators}.
+ *
+ * @hide
+ */
+oneway interface IFaceAuthenticatorsRegisteredCallback {
+ /**
+ * Notifies FaceManager that all of the face authenticators have been registered.
+ *
+ * @param sensors A consolidated list of sensor properties for all of the authenticators.
+ */
+ void onAllAuthenticatorsRegistered(in List<FaceSensorPropertiesInternal> sensors);
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 369248edd580..9b56f43a0f22 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -17,9 +17,11 @@ package android.hardware.face;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricStateListener;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -163,4 +165,11 @@ interface IFaceService {
// hidlSensors must be non-null and empty. See AuthService.java
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors);
+
+ // Adds a callback which gets called when the service registers all of the face
+ // authenticators. The callback is automatically removed after it's invoked.
+ void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback);
+
+ // Registers BiometricStateListener.
+ void registerBiometricStateListener(IBiometricStateListener listener);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index cc7ed183ed64..1ba9a0471c88 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -202,8 +202,10 @@ interface IFingerprintService {
void setSidefpsController(in ISidefpsController controller);
// Registers BiometricStateListener.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerBiometricStateListener(IBiometricStateListener listener);
// Sends a power button pressed event to all listeners.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
oneway void onPowerPressed();
}
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index e38e61122786..3260713a7d14 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -17,6 +17,7 @@
package android.inputmethodservice;
import android.annotation.AnyThread;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -24,9 +25,13 @@ import android.os.RemoteException;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.SelectGesture;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
@@ -35,6 +40,8 @@ import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.InputConnectionCommandHeader;
import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
/**
* A stateless wrapper of {@link com.android.internal.inputmethod.IRemoteInputConnection} to
@@ -591,6 +598,27 @@ final class IRemoteInputConnectionInvoker {
}
}
+ @AnyThread
+ public void performHandwritingGesture(
+ @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
+ @Nullable IntConsumer consumer) {
+ // TODO(b/210039666): implement resultReceiver
+ try {
+ if (gesture instanceof SelectGesture) {
+ mConnection.performHandwritingSelectGesture(
+ createHeader(), (SelectGesture) gesture, null);
+ } else if (gesture instanceof InsertGesture) {
+ mConnection.performHandwritingInsertGesture(
+ createHeader(), (InsertGesture) gesture, null);
+ } else if (gesture instanceof DeleteGesture) {
+ mConnection.performHandwritingDeleteGesture(
+ createHeader(), (DeleteGesture) gesture, null);
+ }
+ } catch (RemoteException e) {
+ // TODO(b/210039666): return result
+ }
+ }
+
/**
* Invokes {@link IRemoteInputConnection#requestCursorUpdates(InputConnectionCommandHeader, int,
* int, AndroidFuture)}.
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 2711c4f0db1f..694293c62bd7 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -17,6 +17,7 @@
package android.inputmethodservice;
import android.annotation.AnyThread;
+import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,7 @@ import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
@@ -41,6 +43,8 @@ import com.android.internal.inputmethod.InputConnectionProtoDumper;
import java.lang.ref.WeakReference;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
/**
* Takes care of remote method invocations of {@link InputConnection} in the IME side.
@@ -411,6 +415,13 @@ final class RemoteInputConnection implements InputConnection {
}
@AnyThread
+ public void performHandwritingGesture(
+ @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
+ @Nullable IntConsumer consumer) {
+ mInvoker.performHandwritingGesture(gesture, executor, consumer);
+ }
+
+ @AnyThread
public boolean requestCursorUpdates(int cursorUpdateMode) {
if (mCancellationGroup.isCanceled()) {
return false;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 26600e256ca1..9d05cec06d01 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2317,6 +2317,11 @@ public abstract class BatteryStats {
public abstract void finishIteratingHistoryLocked();
/**
+ * Return the base time offset for the battery history.
+ */
+ public abstract long getHistoryBaseTime();
+
+ /**
* Returns the number of times the device has been started.
*/
public abstract int getStartCount();
@@ -7601,6 +7606,8 @@ public abstract class BatteryStats {
CHECKIN_VERSION, getParcelVersion(), getStartPlatformVersion(),
getEndPlatformVersion());
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
if (startIteratingHistoryLocked()) {
try {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 62ee408cffbf..fa6b1189f1da 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -76,6 +76,7 @@ interface IUserManager {
String getUserAccount(int userId);
void setUserAccount(int userId, String accountName);
long getUserCreationTime(int userId);
+ boolean isUserSwitcherEnabled(int mUserId);
boolean isRestricted(int userId);
boolean canHaveRestrictedProfile(int userId);
int getUserSerialNumber(int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ef04f641b0ee..738f9c9c4f13 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1995,7 +1995,8 @@ public class UserManager {
/** @hide */
public UserManager(Context context, IUserManager service) {
mService = service;
- mContext = context.getApplicationContext();
+ Context appContext = context.getApplicationContext();
+ mContext = (appContext == null ? context : appContext);
mUserId = context.getUserId();
}
@@ -5246,23 +5247,13 @@ public class UserManager {
})
@UserHandleAware
public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) {
- if (!supportsMultipleUsers()) {
- return false;
- }
- if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId)) {
- return false;
- }
- // If Demo Mode is on, don't show user switcher
- if (isDeviceInDemoMode(mContext)) {
- return false;
- }
- // Check the Settings.Global.USER_SWITCHER_ENABLED that the user can toggle on/off.
- final boolean userSwitcherSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.USER_SWITCHER_ENABLED,
- Resources.getSystem().getBoolean(R.bool.config_showUserSwitcherByDefault) ? 1 : 0)
- != 0;
- if (!userSwitcherSettingOn) {
- return false;
+
+ try {
+ if (!mService.isUserSwitcherEnabled(mUserId)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
}
// The feature is enabled. But is it worth showing?
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8a50e79e8598..14598d558caa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9187,14 +9187,12 @@ public final class Settings {
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
/**
- * The complications that are enabled to be shown over the screensaver by the user. Holds
- * a comma separated list of
- * {@link com.android.settingslib.dream.DreamBackend.ComplicationType}.
+ * Whether complications are enabled to be shown over the screensaver by the user.
*
* @hide
*/
- public static final String SCREENSAVER_ENABLED_COMPLICATIONS =
- "screensaver_enabled_complications";
+ public static final String SCREENSAVER_COMPLICATIONS_ENABLED =
+ "screensaver_complications_enabled";
/**
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 91042bfa3402..a38ef9676072 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -91,6 +91,21 @@ public abstract class NotificationAssistantService extends NotificationListenerS
= "android.service.notification.NotificationAssistantService";
/**
+ * Activity Action: Show notification assistant detail setting page in NAS app.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NOTIFICATION_ASSISTANT_DETAIL_SETTINGS =
+ "android.service.notification.action.NOTIFICATION_ASSISTANT_DETAIL_SETTINGS";
+
+
+ /**
* Data type: int, the feedback rating score provided by user. The score can be any integer
* value depends on the experimental and feedback UX design.
*/
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 95bcda5f7c55..9292e9608261 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -1317,7 +1317,6 @@ final class RemoteSelectionToolbar {
contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
- contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
contentContainer.setClipToOutline(true);
return contentContainer;
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b73ff901052f..0338cebf75c6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -97,10 +97,6 @@ public class FeatureFlagUtils {
/** @hide */
public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping";
- /** Flag to enable/disable guest mode UX changes as mentioned in b/214031645
- * @hide
- */
- public static final String SETTINGS_GUEST_MODE_UX_CHANGES = "settings_guest_mode_ux_changes";
/** Support Clear Calling feature.
* @hide
@@ -150,7 +146,6 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
- DEFAULT_FLAGS.put(SETTINGS_GUEST_MODE_UX_CHANGES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_CLEAR_CALLING, "false");
DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_SIMPLE_CURSOR, "false");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "false");
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 84f04c12cf51..e0f02d6e567a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -130,7 +130,6 @@ public final class SurfaceControl implements Parcelable {
float x, float y);
private static native void nativeSetScale(long transactionObj, long nativeObject,
float x, float y);
- private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long transactionObj,
long nativeObject, Region region);
private static native void nativeSetAlpha(long transactionObj, long nativeObject, float alpha);
@@ -274,6 +273,9 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSanitize(long transactionObject);
private static native void nativeSetDestinationFrame(long transactionObj, long nativeObject,
int l, int t, int r, int b);
+ private static native void nativeSetDefaultApplyToken(IBinder token);
+ private static native IBinder nativeGetDefaultApplyToken();
+
/**
* Transforms that can be applied to buffers as they are displayed to a window.
@@ -2774,6 +2776,22 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ *
+ * @hide
+ */
+ public static void setDefaultApplyToken(IBinder token) {
+ nativeSetDefaultApplyToken(token);
+ }
+
+ /**
+ *
+ * @hide
+ */
+ public static IBinder getDefaultApplyToken() {
+ return nativeGetDefaultApplyToken();
+ }
+
+ /**
* Apply the transaction, clearing it's state, and making it usable
* as a new transaction.
*/
@@ -2954,7 +2972,6 @@ public final class SurfaceControl implements Parcelable {
@IntRange(from = 0) int w, @IntRange(from = 0) int h) {
checkPreconditions(sc);
mResizedSurfaces.put(sc, new Point(w, h));
- nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
return this;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a66427843af0..b6c92e3fd264 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -914,7 +914,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
&& mRequestedVisible;
final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
- getLocationInSurface(mLocation);
+ getLocationInWindow(mLocation);
final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
|| mWindowSpaceTop != mLocation[1];
final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
@@ -925,7 +925,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (creating || formatChanged || sizeChanged || visibleChanged ||
(mUseAlpha && alphaChanged) || windowVisibleChanged ||
positionChanged || layoutSizeChanged || hintChanged) {
- getLocationInWindow(mLocation);
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Changes: creating=" + creating
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9091b7955616..59bc061f54aa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -173,6 +173,7 @@ import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityManager.HighTextContrastChangeListener;
@@ -5318,6 +5319,7 @@ public final class ViewRootImpl implements ViewParent,
}
mAccessibilityInteractionConnectionManager.ensureNoConnection();
+ mAccessibilityInteractionConnectionManager.ensureNoDirectConnection();
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
@@ -9570,6 +9572,14 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ /**
+ * Return the connection ID for the {@link AccessibilityInteractionController} of this instance.
+ * @see AccessibilityNodeInfo#makeQueryableFromAppProcess(View)
+ */
+ public int getDirectAccessibilityConnectionId() {
+ return mAccessibilityInteractionConnectionManager.ensureDirectConnection();
+ }
+
@Override
public boolean showContextMenuForChild(View originalView) {
return false;
@@ -10445,6 +10455,8 @@ public final class ViewRootImpl implements ViewParent,
*/
final class AccessibilityInteractionConnectionManager
implements AccessibilityStateChangeListener {
+ private int mDirectConnectionId = AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID;
+
@Override
public void onAccessibilityStateChanged(boolean enabled) {
if (enabled) {
@@ -10488,6 +10500,21 @@ public final class ViewRootImpl implements ViewParent,
mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
}
}
+
+ public int ensureDirectConnection() {
+ if (mDirectConnectionId == AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID) {
+ mDirectConnectionId = AccessibilityInteractionClient.addDirectConnection(
+ new AccessibilityInteractionConnection(ViewRootImpl.this));
+ }
+ return mDirectConnectionId;
+ }
+
+ public void ensureNoDirectConnection() {
+ if (mDirectConnectionId != AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID) {
+ AccessibilityInteractionClient.removeConnection(mDirectConnectionId);
+ mDirectConnectionId = AccessibilityNodeInfo.UNDEFINED_CONNECTION_ID;
+ }
+ }
}
final class HighContrastTextManager implements HighTextContrastChangeListener {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 68532781f08b..227a8ef6fd60 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -114,6 +114,10 @@ public final class AccessibilityInteractionClient
private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
new SparseArray<>();
+ // Used to generate connection ids for direct app-process connections. Start sufficiently far
+ // enough from the connection ids generated by AccessibilityManagerService.
+ private static int sDirectConnectionIdCounter = 1 << 30;
+
/** List of timestamps which indicate the latest time an a11y service receives a scroll event
from a window, mapping from windowId -> timestamp. */
private static final SparseLongArray sScrollingWindows = new SparseLongArray();
@@ -232,6 +236,12 @@ public final class AccessibilityInteractionClient
return;
}
synchronized (sConnectionCache) {
+ IAccessibilityServiceConnection existingConnection = getConnection(connectionId);
+ if (existingConnection instanceof DirectAccessibilityConnection) {
+ throw new IllegalArgumentException(
+ "Cannot add service connection with id " + connectionId
+ + " which conflicts with existing direct connection.");
+ }
sConnectionCache.put(connectionId, connection);
if (!initializeCache) {
return;
@@ -242,6 +252,33 @@ public final class AccessibilityInteractionClient
}
/**
+ * Adds a new {@link DirectAccessibilityConnection} using the provided
+ * {@link IAccessibilityInteractionConnection} to create a direct connection between
+ * this client and the {@link android.view.ViewRootImpl} for queries inside the app process.
+ *
+ * <p>
+ * See {@link DirectAccessibilityConnection} for supported methods.
+ * </p>
+ *
+ * @param connection The ViewRootImpl's {@link IAccessibilityInteractionConnection}.
+ */
+ public static int addDirectConnection(IAccessibilityInteractionConnection connection) {
+ synchronized (sConnectionCache) {
+ int connectionId = sDirectConnectionIdCounter++;
+ if (getConnection(connectionId) != null) {
+ throw new IllegalArgumentException(
+ "Cannot add direct connection with existing id " + connectionId);
+ }
+ DirectAccessibilityConnection directAccessibilityConnection =
+ new DirectAccessibilityConnection(connection);
+ sConnectionCache.put(connectionId, directAccessibilityConnection);
+ // Do not use AccessibilityCache for this connection, since there is no corresponding
+ // AccessibilityService to handle cache invalidation events.
+ return connectionId;
+ }
+ }
+
+ /**
* Gets a cached associated with the connection id if available.
*
*/
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 953f2615b539..5d527500ff7b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -58,6 +58,7 @@ import android.view.SurfaceView;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.widget.TextView;
import com.android.internal.R;
@@ -82,7 +83,9 @@ import java.util.Objects;
* </p>
* <p>
* Once an accessibility node info is delivered to an accessibility service it is
- * made immutable and calling a state mutation method generates an error.
+ * made immutable and calling a state mutation method generates an error. See
+ * {@link #makeQueryableFromAppProcess(View)} if you would like to inspect the
+ * node tree from the app process for testing or debugging tools.
* </p>
* <p>
* Please refer to {@link android.accessibilityservice.AccessibilityService} for
@@ -1156,8 +1159,8 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param index The child index.
* @return The child node.
*
- * @throws IllegalStateException If called outside of an AccessibilityService.
- *
+ * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
+ * calling {@link #makeQueryableFromAppProcess(View)}.
*/
public AccessibilityNodeInfo getChild(int index) {
return getChild(index, FLAG_PREFETCH_DESCENDANTS_HYBRID);
@@ -1171,7 +1174,8 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param prefetchingStrategy the prefetching strategy.
* @return The child node.
*
- * @throws IllegalStateException If called outside of an AccessibilityService.
+ * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
+ * calling {@link #makeQueryableFromAppProcess(View)}.
*
* @see AccessibilityNodeInfo#getParent(int) for a description of prefetching.
*/
@@ -1893,6 +1897,9 @@ public class AccessibilityNodeInfo implements Parcelable {
* Gets the parent.
*
* @return The parent.
+ *
+ * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
+ * calling {@link #makeQueryableFromAppProcess(View)}.
*/
public AccessibilityNodeInfo getParent() {
enforceSealed();
@@ -1920,7 +1927,8 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param prefetchingStrategy the prefetching strategy.
* @return The parent.
*
- * @throws IllegalStateException If called outside of an AccessibilityService.
+ * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
+ * calling {@link #makeQueryableFromAppProcess(View)}.
*
* @see #FLAG_PREFETCH_ANCESTORS
* @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
@@ -3642,6 +3650,47 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Connects this node to the View's root so that operations on this node can query the entire
+ * {@link AccessibilityNodeInfo} tree and perform accessibility actions on nodes.
+ *
+ * <p>
+ * This is intended for short-lived inspections from testing or debugging tools in the app
+ * process. After calling this method, all nodes linked to this node (children, ancestors, etc.)
+ * are also queryable. Operations on this node tree will only succeed as long as the associated
+ * view hierarchy remains attached to a window.
+ * </p>
+ *
+ * <p>
+ * Calling this method more than once on the same node is a no-op; if you wish to inspect a
+ * different view hierarchy then create a new node from any view in that hierarchy and call this
+ * method on that node.
+ * </p>
+ *
+ * <p>
+ * Testing or debugging tools should create this {@link AccessibilityNodeInfo} node using
+ * {@link View#createAccessibilityNodeInfo()} or {@link AccessibilityNodeProvider} and call this
+ * method, then navigate and interact with the node tree by calling methods on the node.
+ * </p>
+ *
+ * @param view The view that generated this node, or any view in the same view-root hierarchy.
+ * @throws IllegalStateException If called from an {@link AccessibilityService}, or if provided
+ * a {@link View} that is not attached to a window.
+ */
+ public void makeQueryableFromAppProcess(@NonNull View view) {
+ enforceNotSealed();
+ if (mConnectionId != UNDEFINED_CONNECTION_ID) {
+ return;
+ }
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ if (viewRootImpl == null) {
+ throw new IllegalStateException(
+ "Cannot link a node to a view that is not attached to a window.");
+ }
+ setConnectionId(viewRootImpl.getDirectAccessibilityConnectionId());
+ }
+
+ /**
* Sets if this instance is sealed.
*
* @param sealed Whether is sealed.
@@ -3665,15 +3714,21 @@ public class AccessibilityNodeInfo implements Parcelable {
return mSealed;
}
+ private static boolean usingDirectConnection(int connectionId) {
+ return AccessibilityInteractionClient.getConnection(
+ connectionId) instanceof DirectAccessibilityConnection;
+ }
+
/**
- * Enforces that this instance is sealed.
+ * Enforces that this instance is sealed, unless using a {@link DirectAccessibilityConnection}
+ * which allows queries while the node is not sealed.
*
* @throws IllegalStateException If this instance is not sealed.
*
* @hide
*/
protected void enforceSealed() {
- if (!isSealed()) {
+ if (!usingDirectConnection(mConnectionId) && !isSealed()) {
throw new IllegalStateException("Cannot perform this "
+ "action on a not sealed instance.");
}
@@ -4499,7 +4554,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private static boolean canPerformRequestOverConnection(int connectionId,
int windowId, long accessibilityNodeId) {
- return ((windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
+ final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ return ((usingDirectConnection(connectionId) || hasWindowId)
&& (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID)
&& (connectionId != UNDEFINED_CONNECTION_ID));
}
diff --git a/core/java/android/view/accessibility/DirectAccessibilityConnection.java b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
new file mode 100644
index 000000000000..71746ee5dbab
--- /dev/null
+++ b/core/java/android/view/accessibility/DirectAccessibilityConnection.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Matrix;
+import android.graphics.Region;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.view.MagnificationSpec;
+
+/**
+ * Minimal {@link IAccessibilityServiceConnection} implementation that interacts
+ * with the {@link android.view.AccessibilityInteractionController} of a
+ * {@link android.view.ViewRootImpl}.
+ *
+ * <p>
+ * Uses {@link android.view.ViewRootImpl}'s {@link IAccessibilityServiceConnection} that wraps
+ * {@link android.view.AccessibilityInteractionController} within the app process, so that no
+ * interprocess communication is performed.
+ * </p>
+ *
+ * <p>
+ * Only the following methods are supported:
+ * <li>{@link #findAccessibilityNodeInfoByAccessibilityId}</li>
+ * <li>{@link #findAccessibilityNodeInfosByText}</li>
+ * <li>{@link #findAccessibilityNodeInfosByViewId}</li>
+ * <li>{@link #findFocus}</li>
+ * <li>{@link #focusSearch}</li>
+ * <li>{@link #performAccessibilityAction}</li>
+ * </p>
+ *
+ * <p>
+ * Other methods are no-ops and return default values.
+ * </p>
+ */
+class DirectAccessibilityConnection extends IAccessibilityServiceConnection.Default {
+ private final IAccessibilityInteractionConnection mAccessibilityInteractionConnection;
+
+ // Fetch all views, but do not use prefetching/cache since this "connection" does not
+ // receive cache invalidation events (as it is not linked to an AccessibilityService).
+ private static final int FETCH_FLAGS =
+ AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS
+ | AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ private static final MagnificationSpec MAGNIFICATION_SPEC = new MagnificationSpec();
+ private static final int PID = Process.myPid();
+ private static final Region INTERACTIVE_REGION = null;
+ private static final float[] TRANSFORM_MATRIX = new float[9];
+
+ static {
+ Matrix.IDENTITY_MATRIX.getValues(TRANSFORM_MATRIX);
+ }
+
+ DirectAccessibilityConnection(
+ IAccessibilityInteractionConnection accessibilityInteractionConnection) {
+ mAccessibilityInteractionConnection = accessibilityInteractionConnection;
+ }
+
+ @Override
+ public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ long accessibilityNodeId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
+ Bundle arguments) throws RemoteException {
+ mAccessibilityInteractionConnection.findAccessibilityNodeInfoByAccessibilityId(
+ accessibilityNodeId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID,
+ threadId, MAGNIFICATION_SPEC, TRANSFORM_MATRIX, arguments);
+ return new String[0];
+ }
+
+ @Override
+ public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId,
+ long accessibilityNodeId, String text, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId)
+ throws RemoteException {
+ mAccessibilityInteractionConnection.findAccessibilityNodeInfosByText(accessibilityNodeId,
+ text, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
+ MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ return new String[0];
+ }
+
+ @Override
+ public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ long accessibilityNodeId, String viewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId)
+ throws RemoteException {
+ mAccessibilityInteractionConnection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
+ viewId, INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
+ MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ return new String[0];
+ }
+
+ @Override
+ public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId)
+ throws RemoteException {
+ mAccessibilityInteractionConnection.findFocus(accessibilityNodeId, focusType,
+ INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
+ MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ return new String[0];
+ }
+
+ @Override
+ public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId)
+ throws RemoteException {
+ mAccessibilityInteractionConnection.focusSearch(accessibilityNodeId, direction,
+ INTERACTIVE_REGION, interactionId, callback, FETCH_FLAGS, PID, threadId,
+ MAGNIFICATION_SPEC, TRANSFORM_MATRIX);
+ return new String[0];
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
+ int action, Bundle arguments, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId)
+ throws RemoteException {
+ mAccessibilityInteractionConnection.performAccessibilityAction(accessibilityNodeId, action,
+ arguments, interactionId, callback, FETCH_FLAGS, PID, threadId);
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiModel.kt b/core/java/android/view/inputmethod/DeleteGesture.aidl
index 1b7332265b7a..e9f31dd470ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiModel.kt
+++ b/core/java/android/view/inputmethod/DeleteGesture.aidl
@@ -14,16 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.wifi.data.model
+package android.view.inputmethod;
-/** Provides information about the current wifi state. */
-data class WifiModel(
- /** See [android.net.wifi.WifiInfo.ssid]. */
- val ssid: String? = null,
- /** See [android.net.wifi.WifiInfo.isPasspointAp]. */
- val isPasspointAccessPoint: Boolean = false,
- /** See [android.net.wifi.WifiInfo.isOsuAp]. */
- val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
- /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
- val passpointProviderFriendlyName: String? = null,
-)
+parcelable DeleteGesture; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/DeleteGesture.java b/core/java/android/view/inputmethod/DeleteGesture.java
new file mode 100644
index 000000000000..257254e5737a
--- /dev/null
+++ b/core/java/android/view/inputmethod/DeleteGesture.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * A sub-class of {@link HandwritingGesture} for deleting an area of text.
+ * This class holds the information required for deletion of text in
+ * toolkit widgets like {@link TextView}.
+ */
+public final class DeleteGesture extends HandwritingGesture implements Parcelable {
+
+ private @Granularity int mGranularity;
+ private RectF mArea;
+
+ private DeleteGesture(@Granularity int granularity, RectF area, String fallbackText) {
+ mArea = area;
+ mGranularity = granularity;
+ mFallbackText = fallbackText;
+ }
+
+ private DeleteGesture(@NonNull final Parcel source) {
+ mFallbackText = source.readString8();
+ mGranularity = source.readInt();
+ mArea = source.readTypedObject(RectF.CREATOR);
+ }
+
+ /**
+ * Returns Granular level on which text should be operated.
+ * @see HandwritingGesture#GRANULARITY_CHARACTER
+ * @see HandwritingGesture#GRANULARITY_WORD
+ */
+ @Granularity
+ public int getGranularity() {
+ return mGranularity;
+ }
+
+ /**
+ * Returns the deletion area {@link RectF} in screen coordinates.
+ *
+ * Getter for deletion area set with {@link DeleteGesture.Builder#setDeletionArea(RectF)}.
+ * {@code null} if area was not set.
+ */
+ @NonNull
+ public RectF getDeletionArea() {
+ return mArea;
+ }
+
+ /**
+ * Builder for {@link DeleteGesture}. This class is not designed to be thread-safe.
+ */
+ public static final class Builder {
+ private int mGranularity;
+ private RectF mArea;
+ private String mFallbackText;
+
+ /**
+ * Set text deletion granularity. Intersecting words/characters will be
+ * included in the operation.
+ * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or
+ * {@link HandwritingGesture#GRANULARITY_CHARACTER}.
+ * @return {@link Builder}.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setGranularity(@Granularity int granularity) {
+ mGranularity = granularity;
+ return this;
+ }
+
+ /**
+ * Set rectangular single/multiline text deletion area intersecting with text.
+ *
+ * The resulting deletion would be performed for all text intersecting rectangle. The
+ * deletion includes the first word/character in the rectangle, and the last
+ * word/character in the rectangle, and includes everything in between even if it's not
+ * in the rectangle.
+ *
+ * Intersection is determined using
+ * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes
+ * all the words with their width/height center included in the deletion rectangle.
+ * @param area {@link RectF} (in screen coordinates) for which text will be deleted.
+ * @see HandwritingGesture#GRANULARITY_WORD
+ * @see HandwritingGesture#GRANULARITY_CHARACTER
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setDeletionArea(@NonNull RectF area) {
+ mArea = area;
+ return this;
+ }
+
+ /**
+ * Set fallback text that will be committed at current cursor position if there is no
+ * applicable text beneath the area of gesture.
+ * @param fallbackText text to set
+ */
+ @NonNull
+ public Builder setFallbackText(@Nullable String fallbackText) {
+ mFallbackText = fallbackText;
+ return this;
+ }
+
+ /**
+ * @return {@link DeleteGesture} using parameters in this {@link DeleteGesture.Builder}.
+ * @throws IllegalArgumentException if one or more positional parameters are not specified.
+ */
+ @NonNull
+ public DeleteGesture build() {
+ if (mArea == null || mArea.isEmpty()) {
+ throw new IllegalArgumentException("Deletion area must be set.");
+ }
+ if (mGranularity <= GRANULARITY_UNDEFINED) {
+ throw new IllegalArgumentException("Deletion granularity must be set.");
+ }
+ return new DeleteGesture(mGranularity, mArea, mFallbackText);
+ }
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final @android.annotation.NonNull Creator<DeleteGesture> CREATOR =
+ new Creator<DeleteGesture>() {
+ @Override
+ public DeleteGesture createFromParcel(Parcel source) {
+ return new DeleteGesture(source);
+ }
+
+ @Override
+ public DeleteGesture[] newArray(int size) {
+ return new DeleteGesture[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mArea, mGranularity, mFallbackText);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DeleteGesture)) return false;
+
+ DeleteGesture that = (DeleteGesture) o;
+
+ if (mGranularity != that.mGranularity) return false;
+ if (!Objects.equals(mFallbackText, that.mFallbackText)) return false;
+ return Objects.equals(mArea, that.mArea);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mFallbackText);
+ dest.writeInt(mGranularity);
+ dest.writeTypedObject(mArea, flags);
+ }
+}
diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java
new file mode 100644
index 000000000000..15824aeb0aeb
--- /dev/null
+++ b/core/java/android/view/inputmethod/HandwritingGesture.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.graphics.RectF;
+import android.inputmethodservice.InputMethodService;
+import android.view.MotionEvent;
+
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
+
+/**
+ * Base class for Stylus handwriting gesture.
+ *
+ * During a stylus handwriting session, user can perform a stylus gesture operation like
+ * {@link SelectGesture}, {@link DeleteGesture}, {@link InsertGesture} on an
+ * area of text. IME is responsible for listening to Stylus {@link MotionEvent} using
+ * {@link InputMethodService#onStylusHandwritingMotionEvent} and interpret if it can translate to a
+ * gesture operation.
+ * While creating Gesture operations {@link SelectGesture}, {@link DeleteGesture},
+ * , {@code Granularity} helps pick the correct granular level of text like word level
+ * {@link #GRANULARITY_WORD}, or character level {@link #GRANULARITY_CHARACTER}.
+ *
+ * @see InputConnection#performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)
+ * @see InputMethodService#onStartStylusHandwriting()
+ */
+public abstract class HandwritingGesture {
+
+ HandwritingGesture() {}
+
+ static final int GRANULARITY_UNDEFINED = 0;
+
+ /**
+ * Operate text per word basis. e.g. if selection includes width-wise center of the word,
+ * whole word is selected.
+ * <p> Strategy of operating at a granular level is maintained in the UI toolkit.
+ * A character/word/line is included if its center is within the gesture rectangle.
+ * e.g. if a selection {@link RectF} with {@link #GRANULARITY_WORD} includes width-wise
+ * center of the word, it should be selected.
+ * Similarly, text in a line should be included in the operation if rectangle includes
+ * line height center.</p>
+ * Refer to https://www.unicode.org/reports/tr29/#Word_Boundaries for more detail on how word
+ * breaks are decided.
+ */
+ public static final int GRANULARITY_WORD = 1;
+
+ /**
+ * Operate on text per character basis. i.e. each character is selected based on its
+ * intersection with selection rectangle.
+ * <p> Strategy of operating at a granular level is maintained in the UI toolkit.
+ * A character/word/line is included if its center is within the gesture rectangle.
+ * e.g. if a selection {@link RectF} with {@link #GRANULARITY_CHARACTER} includes width-wise
+ * center of the character, it should be selected.
+ * Similarly, text in a line should be included in the operation if rectangle includes
+ * line height center.</p>
+ */
+ public static final int GRANULARITY_CHARACTER = 2;
+
+ /**
+ * Granular level on which text should be operated.
+ */
+ @IntDef({GRANULARITY_CHARACTER, GRANULARITY_WORD})
+ @interface Granularity {}
+
+ @Nullable
+ String mFallbackText;
+
+ /**
+ * The fallback text that will be committed at current cursor position if there is no applicable
+ * text beneath the area of gesture.
+ * For example, select can fail if gesture is drawn over area that has no text beneath.
+ * example 2: join can fail if the gesture is drawn over text but there is no whitespace.
+ */
+ @Nullable
+ public String getFallbackText() {
+ return mFallbackText;
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index dac1be6f5a13..7b0270a1859f 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -31,6 +32,8 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
/**
* The InputConnection interface is the communication channel from an
@@ -968,6 +971,17 @@ public interface InputConnection {
boolean performPrivateCommand(String action, Bundle data);
/**
+ * Perform a handwriting gesture on text.
+ *
+ * @param gesture the gesture to perform
+ * @param executor if the caller passes a non-null consumer TODO(b/210039666): complete doc
+ * @param consumer if the caller passes a non-null receiver, the editor must invoke this
+ */
+ default void performHandwritingGesture(
+ @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
+ @Nullable IntConsumer consumer) {}
+
+ /**
* The editor is requested to call
* {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
* once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 7a88a75f93ad..56beddf2ef38 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,6 +26,9 @@ import android.view.KeyEvent;
import com.android.internal.util.Preconditions;
+import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
+
/**
* <p>Wrapper class for proxying calls to another InputConnection. Subclass and have fun!
*/
@@ -323,6 +327,17 @@ public class InputConnectionWrapper implements InputConnection {
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public void performHandwritingGesture(
+ @NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
+ @Nullable IntConsumer consumer) {
+ mTarget.performHandwritingGesture(gesture, executor, consumer);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return mTarget.requestCursorUpdates(cursorUpdateMode);
}
diff --git a/core/java/android/view/inputmethod/InsertGesture.aidl b/core/java/android/view/inputmethod/InsertGesture.aidl
new file mode 100644
index 000000000000..9cdb14a8b3ab
--- /dev/null
+++ b/core/java/android/view/inputmethod/InsertGesture.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable InsertGesture; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/InsertGesture.java b/core/java/android/view/inputmethod/InsertGesture.java
new file mode 100644
index 000000000000..2cf015a33283
--- /dev/null
+++ b/core/java/android/view/inputmethod/InsertGesture.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.graphics.PointF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * A sub-class of {@link HandwritingGesture} for inserting text at the defined insertion point.
+ * This class holds the information required for insertion of text in
+ * toolkit widgets like {@link TextView}.
+ */
+public final class InsertGesture extends HandwritingGesture implements Parcelable {
+
+ private String mTextToInsert;
+ private PointF mPoint;
+
+ private InsertGesture(String text, PointF point, String fallbackText) {
+ mPoint = point;
+ mTextToInsert = text;
+ mFallbackText = fallbackText;
+ }
+
+ private InsertGesture(final Parcel source) {
+ mFallbackText = source.readString8();
+ mTextToInsert = source.readString8();
+ mPoint = source.readTypedObject(PointF.CREATOR);
+ }
+
+ /** Returns the text that will be inserted at {@link #getInsertionPoint()} **/
+ @Nullable
+ public String getTextToInsert() {
+ return mTextToInsert;
+ }
+
+ /**
+ * Returns the insertion point {@link PointF} (in screen coordinates) where
+ * {@link #getTextToInsert()} will be inserted.
+ */
+ @Nullable
+ public PointF getInsertionPoint() {
+ return mPoint;
+ }
+
+ /**
+ * Builder for {@link InsertGesture}. This class is not designed to be thread-safe.
+ */
+ public static final class Builder {
+ private String mText;
+ private PointF mPoint;
+ private String mFallbackText;
+
+ /** set the text that will be inserted at {@link #setInsertionPoint(PointF)} **/
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setTextToInsert(@NonNull String text) {
+ mText = text;
+ return this;
+ }
+
+ /**
+ * Sets the insertion point (in screen coordinates) where {@link #setTextToInsert(String)}
+ * should be inserted.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setInsertionPoint(@NonNull PointF point) {
+ mPoint = point;
+ return this;
+ }
+
+ /**
+ * Set fallback text that will be committed at current cursor position if there is no
+ * applicable text beneath the area of gesture.
+ * @param fallbackText text to set
+ */
+ @NonNull
+ public Builder setFallbackText(@Nullable String fallbackText) {
+ mFallbackText = fallbackText;
+ return this;
+ }
+
+ /**
+ * @return {@link InsertGesture} using parameters in this {@link InsertGesture.Builder}.
+ * @throws IllegalArgumentException if one or more positional parameters are not specified.
+ */
+ @NonNull
+ public InsertGesture build() {
+ if (mPoint == null) {
+ throw new IllegalArgumentException("Insertion point must be set.");
+ }
+ if (TextUtils.isEmpty(mText)) {
+ throw new IllegalArgumentException("Text to insert must be non-empty.");
+ }
+ return new InsertGesture(mText, mPoint, mFallbackText);
+ }
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final @android.annotation.NonNull Creator<InsertGesture> CREATOR =
+ new Creator<InsertGesture>() {
+ @Override
+ public InsertGesture createFromParcel(Parcel source) {
+ return new InsertGesture(source);
+ }
+
+ @Override
+ public InsertGesture[] newArray(int size) {
+ return new InsertGesture[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPoint, mTextToInsert, mFallbackText);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof InsertGesture)) return false;
+
+ InsertGesture that = (InsertGesture) o;
+
+ if (!Objects.equals(mFallbackText, that.mFallbackText)) return false;
+ if (!Objects.equals(mTextToInsert, that.mTextToInsert)) return false;
+ return Objects.equals(mPoint, that.mPoint);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mFallbackText);
+ dest.writeString8(mTextToInsert);
+ dest.writeTypedObject(mPoint, flags);
+ }
+}
diff --git a/core/java/android/view/inputmethod/SelectGesture.aidl b/core/java/android/view/inputmethod/SelectGesture.aidl
new file mode 100644
index 000000000000..65da4f340e8c
--- /dev/null
+++ b/core/java/android/view/inputmethod/SelectGesture.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable SelectGesture; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/SelectGesture.java b/core/java/android/view/inputmethod/SelectGesture.java
new file mode 100644
index 000000000000..f3cd71e1eed9
--- /dev/null
+++ b/core/java/android/view/inputmethod/SelectGesture.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * A sub-class of {@link HandwritingGesture} for selecting an area of text.
+ * This class holds the information required for selection of text in
+ * toolkit widgets like {@link TextView}.
+ */
+public final class SelectGesture extends HandwritingGesture implements Parcelable {
+
+ private @Granularity int mGranularity;
+ private RectF mArea;
+
+ private SelectGesture(int granularity, RectF area, String fallbackText) {
+ mArea = area;
+ mGranularity = granularity;
+ mFallbackText = fallbackText;
+ }
+
+ private SelectGesture(@NonNull Parcel source) {
+ mFallbackText = source.readString8();
+ mGranularity = source.readInt();
+ mArea = source.readTypedObject(RectF.CREATOR);
+ }
+
+ /**
+ * Returns Granular level on which text should be operated.
+ * @see #GRANULARITY_CHARACTER
+ * @see #GRANULARITY_WORD
+ */
+ @Granularity
+ public int getGranularity() {
+ return mGranularity;
+ }
+
+ /**
+ * Returns the Selection area {@link RectF} in screen coordinates.
+ *
+ * Getter for selection area set with {@link Builder#setSelectionArea(RectF)}. {@code null}
+ * if area was not set.
+ */
+ @NonNull
+ public RectF getSelectionArea() {
+ return mArea;
+ }
+
+
+ /**
+ * Builder for {@link SelectGesture}. This class is not designed to be thread-safe.
+ */
+ public static final class Builder {
+ private int mGranularity;
+ private RectF mArea;
+ private String mFallbackText;
+
+ /**
+ * Define text selection granularity. Intersecting words/characters will be
+ * included in the operation.
+ * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or
+ * {@link HandwritingGesture#GRANULARITY_CHARACTER}.
+ * @return {@link Builder}.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setGranularity(@Granularity int granularity) {
+ mGranularity = granularity;
+ return this;
+ }
+
+ /**
+ * Set rectangular single/multiline text selection area intersecting with text.
+ *
+ * The resulting selection would be performed for all text intersecting rectangle. The
+ * selection includes the first word/character in the rectangle, and the last
+ * word/character in the rectangle, and includes everything in between even if it's not
+ * in the rectangle.
+ *
+ * Intersection is determined using
+ * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes
+ * all the words with their width/height center included in the selection rectangle.
+ * @param area {@link RectF} (in screen coordinates) for which text will be selection.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setSelectionArea(@NonNull RectF area) {
+ mArea = area;
+ return this;
+ }
+
+ /**
+ * Set fallback text that will be committed at current cursor position if there is no
+ * applicable text beneath the area of gesture.
+ * @param fallbackText text to set
+ */
+ @NonNull
+ public Builder setFallbackText(@Nullable String fallbackText) {
+ mFallbackText = fallbackText;
+ return this;
+ }
+
+ /**
+ * @return {@link SelectGesture} using parameters in this {@link InsertGesture.Builder}.
+ * @throws IllegalArgumentException if one or more positional parameters are not specified.
+ */
+ @NonNull
+ public SelectGesture build() {
+ if (mArea == null || mArea.isEmpty()) {
+ throw new IllegalArgumentException("Selection area must be set.");
+ }
+ if (mGranularity <= GRANULARITY_UNDEFINED) {
+ throw new IllegalArgumentException("Selection granularity must be set.");
+ }
+ return new SelectGesture(mGranularity, mArea, mFallbackText);
+ }
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final @android.annotation.NonNull Parcelable.Creator<SelectGesture> CREATOR =
+ new Parcelable.Creator<SelectGesture>() {
+ @Override
+ public SelectGesture createFromParcel(Parcel source) {
+ return new SelectGesture(source);
+ }
+
+ @Override
+ public SelectGesture[] newArray(int size) {
+ return new SelectGesture[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGranularity, mArea, mFallbackText);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SelectGesture)) return false;
+
+ SelectGesture that = (SelectGesture) o;
+
+ if (mGranularity != that.mGranularity) return false;
+ if (!Objects.equals(mFallbackText, that.mFallbackText)) return false;
+ return Objects.equals(mArea, that.mArea);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mFallbackText);
+ dest.writeInt(mGranularity);
+ dest.writeTypedObject(mArea, flags);
+ }
+}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index e243aae81da4..efe3fd468e37 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -31,6 +31,7 @@ import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.SoundEffectConstants;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ExpandableListConnector.PositionMetadata;
import com.android.internal.R;
@@ -1144,6 +1145,24 @@ public class ExpandableListView extends ListView {
return new ExpandableListContextMenuInfo(view, packedPosition, id);
}
+ /** @hide */
+ @Override
+ public void onInitializeAccessibilityNodeInfoForItem(
+ View view, int position, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfoForItem(view, position, info);
+
+ final PositionMetadata metadata = mConnector.getUnflattenedPos(position);
+ if (metadata.position.type == ExpandableListPosition.GROUP) {
+ if (isGroupExpanded(metadata.position.groupPos)) {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+ } else {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
+ }
+ }
+
+ metadata.recycle();
+ }
+
/**
* Gets the ID of the group or child at the given <code>position</code>.
* This is useful since there is no ListAdapter ID -> ExpandableListAdapter
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 8407d10bc3ea..884ca77ea377 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -16,8 +16,10 @@
package android.window;
+import android.os.IBinder;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
+import android.window.WindowContainerTransaction;
/** @hide */
interface ITaskFragmentOrganizerController {
@@ -46,8 +48,15 @@ interface ITaskFragmentOrganizerController {
void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer, int taskId);
/**
- * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
- * only occupies a portion of Task bounds.
- */
+ * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+ * only occupies a portion of Task bounds.
+ */
boolean isActivityEmbedded(in IBinder activityToken);
+
+ /**
+ * Notifies the server that the organizer has finished handling the given transaction. The
+ * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
+ */
+ void onTransactionHandled(in ITaskFragmentOrganizer organizer, in IBinder transactionToken,
+ in WindowContainerTransaction wct);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index cd15df84debd..7359172289cd 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -26,7 +26,6 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.Intent;
import android.content.res.Configuration;
@@ -141,6 +140,28 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
/**
+ * Notifies the server that the organizer has finished handling the given transaction. The
+ * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
+ *
+ * @param transactionToken {@link TaskFragmentTransaction#getTransactionToken()} from
+ * {@link #onTransactionReady(TaskFragmentTransaction)}
+ * @param wct {@link WindowContainerTransaction} that the server should apply for
+ * update of the transaction.
+ * @see com.android.server.wm.WindowOrganizerController#enforceTaskPermission for permission
+ * requirement.
+ * @hide
+ */
+ public void onTransactionHandled(@NonNull IBinder transactionToken,
+ @NonNull WindowContainerTransaction wct) {
+ wct.setTaskFragmentOrganizer(mInterface);
+ try {
+ getController().onTransactionHandled(mInterface, transactionToken, wct);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called when a TaskFragment is created and organized by this organizer.
*
* @param wct The {@link WindowContainerTransaction} to make any changes with if needed. No
@@ -227,12 +248,8 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
/**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
- * Note: {@link WindowOrganizer#applyTransaction} permission requirement is conditional for
- * {@link TaskFragmentOrganizer}.
- * @see com.android.server.wm.WindowOrganizerController#enforceTaskPermission
* @hide
*/
- @SuppressLint("AndroidFrameworkRequiresPermission")
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
@@ -274,8 +291,9 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
"Unknown TaskFragmentEvent=" + change.getType());
}
}
- // TODO(b/240519866): notify TaskFragmentOrganizerController that the transition is done.
- applyTransaction(wct);
+
+ // Notify the server, and the server should apply the WindowContainerTransaction.
+ onTransactionHandled(transaction.getTransactionToken(), wct);
}
@Override
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 07e8e8c473c6..84a5fea9f57f 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
@@ -41,19 +42,31 @@ import java.util.List;
*/
public final class TaskFragmentTransaction implements Parcelable {
+ /** Unique token to represent this transaction. */
+ private final IBinder mTransactionToken;
+
+ /** Changes in this transaction. */
private final ArrayList<Change> mChanges = new ArrayList<>();
- public TaskFragmentTransaction() {}
+ public TaskFragmentTransaction() {
+ mTransactionToken = new Binder();
+ }
private TaskFragmentTransaction(Parcel in) {
+ mTransactionToken = in.readStrongBinder();
in.readTypedList(mChanges, Change.CREATOR);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mTransactionToken);
dest.writeTypedList(mChanges);
}
+ public IBinder getTransactionToken() {
+ return mTransactionToken;
+ }
+
/** Adds a {@link Change} to this transaction. */
public void addChange(@Nullable Change change) {
if (change != null) {
@@ -74,7 +87,9 @@ public final class TaskFragmentTransaction implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("TaskFragmentTransaction{changes=[");
+ sb.append("TaskFragmentTransaction{token=");
+ sb.append(mTransactionToken);
+ sb.append(" changes=[");
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
sb.append(',');
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index fcdcb2dadb54..8f6bc438ed9f 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -217,7 +217,11 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable {
case TYPE_HEADER_ALL_OTHERS:
TextView textView = (TextView) itemView;
if (itemType == TYPE_HEADER_SUGGESTED) {
- setTextTo(textView, R.string.language_picker_section_suggested);
+ if (mCountryMode) {
+ setTextTo(textView, R.string.language_picker_regions_section_suggested);
+ } else {
+ setTextTo(textView, R.string.language_picker_section_suggested);
+ }
} else {
if (mCountryMode) {
setTextTo(textView, R.string.region_picker_section_all);
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index a7dd6f13b02d..7a219c61bafb 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -17,11 +17,15 @@
package com.android.internal.inputmethod;
import android.os.Bundle;
+import android.os.ResultReceiver;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.SelectGesture;
import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
@@ -86,6 +90,15 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void performPrivateCommand(in InputConnectionCommandHeader header, String action,
in Bundle data);
+ void performHandwritingSelectGesture(in InputConnectionCommandHeader header,
+ in SelectGesture gesture, in ResultReceiver resultReceiver);
+
+ void performHandwritingInsertGesture(in InputConnectionCommandHeader header,
+ in InsertGesture gesture, in ResultReceiver resultReceiver);
+
+ void performHandwritingDeleteGesture(in InputConnectionCommandHeader header,
+ in DeleteGesture gesture, in ResultReceiver resultReceiver);
+
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index b63ce1b22cdb..c65a69f05797 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -31,6 +31,7 @@ import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.ResultReceiver;
import android.os.Trace;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -39,11 +40,15 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.DumpableInputConnection;
import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.SelectGesture;
import android.view.inputmethod.TextAttribute;
import android.view.inputmethod.TextSnapshot;
@@ -970,6 +975,67 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub
@Dispatching(cancellable = true)
@Override
+ public void performHandwritingSelectGesture(
+ InputConnectionCommandHeader header, SelectGesture gesture,
+ ResultReceiver resultReceiver) {
+ performHandwritingGestureInternal(header, gesture, resultReceiver);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void performHandwritingInsertGesture(
+ InputConnectionCommandHeader header, InsertGesture gesture,
+ ResultReceiver resultReceiver) {
+ performHandwritingGestureInternal(header, gesture, resultReceiver);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
+ public void performHandwritingDeleteGesture(
+ InputConnectionCommandHeader header, DeleteGesture gesture,
+ ResultReceiver resultReceiver) {
+ performHandwritingGestureInternal(header, gesture, resultReceiver);
+ }
+
+ private <T extends HandwritingGesture> void performHandwritingGestureInternal(
+ InputConnectionCommandHeader header, T gesture, ResultReceiver resultReceiver) {
+ dispatchWithTracing("performHandwritingGesture", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performHandwritingGesture on inactive InputConnection");
+ return;
+ }
+ // TODO(b/210039666): implement resultReceiver
+ ic.performHandwritingGesture(gesture, null, null);
+ });
+ }
+
+ /**
+ * Dispatches {@link InputConnection#requestCursorUpdates(int)}.
+ *
+ * <p>This method is intended to be called only from {@link InputMethodManager}.</p>
+ * @param cursorUpdateMode the mode for {@link InputConnection#requestCursorUpdates(int, int)}
+ * @param cursorUpdateFilter the filter for
+ * {@link InputConnection#requestCursorUpdates(int, int)}
+ * @param imeDisplayId displayId on which IME is displayed.
+ */
+ @Dispatching(cancellable = true)
+ public void requestCursorUpdatesFromImm(int cursorUpdateMode, int cursorUpdateFilter,
+ int imeDisplayId) {
+ final int currentSessionId = mCurrentSessionId.get();
+ dispatchWithTracing("requestCursorUpdatesFromImm", () -> {
+ if (currentSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ requestCursorUpdatesInternal(cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 72de78c148f8..fc4e041058b1 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -87,6 +87,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UiThread;
import android.annotation.WorkerThread;
+import android.app.ActivityThread;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -292,7 +293,10 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL,
};
- private static volatile InteractionJankMonitor sInstance;
+ private static class InstanceHolder {
+ public static final InteractionJankMonitor INSTANCE =
+ new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
+ }
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
this::updateProperties;
@@ -384,15 +388,7 @@ public class InteractionJankMonitor {
* @return instance of InteractionJankMonitor
*/
public static InteractionJankMonitor getInstance() {
- // Use DCL here since this method might be invoked very often.
- if (sInstance == null) {
- synchronized (InteractionJankMonitor.class) {
- if (sInstance == null) {
- sInstance = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
- }
- }
- }
- return sInstance;
+ return InstanceHolder.INSTANCE;
}
/**
@@ -402,6 +398,11 @@ public class InteractionJankMonitor {
*/
@VisibleForTesting
public InteractionJankMonitor(@NonNull HandlerThread worker) {
+ // Check permission early.
+ DeviceConfig.enforceReadPermission(
+ ActivityThread.currentApplication().getApplicationContext(),
+ DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR);
+
mRunningTrackers = new SparseArray<>();
mTimeoutActions = new SparseArray<>();
mWorker = worker;
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 6909965edcd8..962870e733f7 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -17,35 +17,25 @@
package com.android.internal.os;
import android.annotation.Nullable;
-import android.os.BatteryManager;
-import android.os.BatteryStats.HistoryItem;
-import android.os.BatteryStats.HistoryStepDetails;
-import android.os.BatteryStats.HistoryTag;
+import android.os.BatteryStats;
import android.os.Parcel;
-import android.os.ParcelFormatException;
-import android.os.Process;
import android.os.StatFs;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ParseUtils;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Supplier;
/**
* BatteryStatsHistory encapsulates battery history files.
@@ -66,62 +56,57 @@ import java.util.concurrent.locks.ReentrantLock;
* All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by
* locks on BatteryStatsImpl object.
*/
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class BatteryStatsHistory {
private static final boolean DEBUG = false;
private static final String TAG = "BatteryStatsHistory";
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- private static final int VERSION = 208;
+ public static final int VERSION = 208;
- private static final String HISTORY_DIR = "battery-history";
- private static final String FILE_SUFFIX = ".bin";
+ public static final String HISTORY_DIR = "battery-history";
+ public static final String FILE_SUFFIX = ".bin";
private static final int MIN_FREE_SPACE = 100 * 1024 * 1024;
-
// Part of initial delta int that specifies the time delta.
- static final int DELTA_TIME_MASK = 0x7ffff;
- static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long
- static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int
- static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update.
+ public static final int DELTA_TIME_MASK = 0x7ffff;
+ public static final int DELTA_TIME_LONG = 0x7ffff; // The delta is a following long
+ public static final int DELTA_TIME_INT = 0x7fffe; // The delta is a following int
+ public static final int DELTA_TIME_ABS = 0x7fffd; // Following is an entire abs update.
// Flag in delta int: a new battery level int follows.
- static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000;
+ public static final int DELTA_BATTERY_LEVEL_FLAG = 0x00080000;
// Flag in delta int: a new full state and battery status int follows.
- static final int DELTA_STATE_FLAG = 0x00100000;
+ public static final int DELTA_STATE_FLAG = 0x00100000;
// Flag in delta int: a new full state2 int follows.
- static final int DELTA_STATE2_FLAG = 0x00200000;
+ public static final int DELTA_STATE2_FLAG = 0x00200000;
// Flag in delta int: contains a wakelock or wakeReason tag.
- static final int DELTA_WAKELOCK_FLAG = 0x00400000;
+ public static final int DELTA_WAKELOCK_FLAG = 0x00400000;
// Flag in delta int: contains an event description.
- static final int DELTA_EVENT_FLAG = 0x00800000;
+ public static final int DELTA_EVENT_FLAG = 0x00800000;
// Flag in delta int: contains the battery charge count in uAh.
- static final int DELTA_BATTERY_CHARGE_FLAG = 0x01000000;
+ public static final int DELTA_BATTERY_CHARGE_FLAG = 0x01000000;
// These upper bits are the frequently changing state bits.
- static final int DELTA_STATE_MASK = 0xfe000000;
+ public static final int DELTA_STATE_MASK = 0xfe000000;
// These are the pieces of battery state that are packed in to the upper bits of
// the state int that have been packed in to the first delta int. They must fit
// in STATE_BATTERY_MASK.
- static final int STATE_BATTERY_MASK = 0xff000000;
- static final int STATE_BATTERY_STATUS_MASK = 0x00000007;
- static final int STATE_BATTERY_STATUS_SHIFT = 29;
- static final int STATE_BATTERY_HEALTH_MASK = 0x00000007;
- static final int STATE_BATTERY_HEALTH_SHIFT = 26;
- static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
- static final int STATE_BATTERY_PLUG_SHIFT = 24;
+ public static final int STATE_BATTERY_MASK = 0xff000000;
+ public static final int STATE_BATTERY_STATUS_MASK = 0x00000007;
+ public static final int STATE_BATTERY_STATUS_SHIFT = 29;
+ public static final int STATE_BATTERY_HEALTH_MASK = 0x00000007;
+ public static final int STATE_BATTERY_HEALTH_SHIFT = 26;
+ public static final int STATE_BATTERY_PLUG_MASK = 0x00000003;
+ public static final int STATE_BATTERY_PLUG_SHIFT = 24;
// We use the low bit of the battery state int to indicate that we have full details
// from a battery level change.
- static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
+ public static final int BATTERY_DELTA_LEVEL_FLAG = 0x00000001;
// Flag in history tag index: indicates that this is the first occurrence of this tag,
// therefore the tag value is written in the parcel
- static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
+ public static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
+ @Nullable
+ private final Supplier<Integer> mMaxHistoryFiles;
private final Parcel mHistoryBuffer;
- private final File mSystemDir;
- private final HistoryStepDetailsCalculator mStepDetailsCalculator;
private final File mHistoryDir;
- private final Clock mClock;
-
- private int mMaxHistoryFiles;
- private int mMaxHistoryBufferSize;
-
/**
* The active history file that the history buffer is backed up into.
*/
@@ -159,77 +144,19 @@ public class BatteryStatsHistory {
*/
private int mParcelIndex = 0;
- private final ReentrantLock mWriteLock = new ReentrantLock();
-
- private final HistoryItem mHistoryCur = new HistoryItem();
-
- private boolean mHaveBatteryLevel;
- private boolean mRecordingHistory;
-
- private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
- private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
-
- private final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
- private SparseArray<HistoryTag> mHistoryTags;
- private final HistoryItem mHistoryLastWritten = new HistoryItem();
- private final HistoryItem mHistoryLastLastWritten = new HistoryItem();
- private final HistoryItem mHistoryAddTmp = new HistoryItem();
- private int mNextHistoryTagIdx = 0;
- private int mNumHistoryTagChars = 0;
- private int mHistoryBufferLastPos = -1;
- private int mActiveHistoryStates = 0xffffffff;
- private int mActiveHistoryStates2 = 0xffffffff;
- private long mLastHistoryElapsedRealtimeMs = 0;
- private long mTrackRunningHistoryElapsedRealtimeMs = 0;
- private long mTrackRunningHistoryUptimeMs = 0;
- private long mHistoryBaseTimeMs;
-
- private byte mLastHistoryStepLevel = 0;
-
- private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator;
-
- /**
- * A delegate responsible for computing additional details for a step in battery history.
- */
- public interface HistoryStepDetailsCalculator {
- /**
- * Returns additional details for the current history step or null.
- */
- @Nullable
- HistoryStepDetails getHistoryStepDetails();
-
- /**
- * Resets the calculator to get ready for a new battery session
- */
- void clear();
- }
-
/**
* Constructor
*
- * @param systemDir typically /data/system
- * @param maxHistoryFiles the largest number of history buffer files to keep
- * @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps
+ * @param historyBuffer The in-memory history buffer.
+ * @param systemDir typically /data/system
+ * @param maxHistoryFiles the largest number of history buffer files to keep
*/
- public BatteryStatsHistory(File systemDir, int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
- this(Parcel.obtain(), systemDir, maxHistoryFiles, maxHistoryBufferSize,
- stepDetailsCalculator, clock);
- initHistoryBuffer();
- }
-
- @VisibleForTesting
public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
- int maxHistoryFiles, int maxHistoryBufferSize,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
+ Supplier<Integer> maxHistoryFiles) {
mHistoryBuffer = historyBuffer;
- mSystemDir = systemDir;
+ mHistoryDir = new File(systemDir, HISTORY_DIR);
mMaxHistoryFiles = maxHistoryFiles;
- mMaxHistoryBufferSize = maxHistoryBufferSize;
- mStepDetailsCalculator = stepDetailsCalculator;
- mClock = clock;
- mHistoryDir = new File(systemDir, HISTORY_DIR);
mHistoryDir.mkdirs();
if (!mHistoryDir.exists()) {
Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath());
@@ -265,81 +192,19 @@ public class BatteryStatsHistory {
}
}
- public BatteryStatsHistory(HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
- mStepDetailsCalculator = stepDetailsCalculator;
- mClock = clock;
-
- mHistoryBuffer = Parcel.obtain();
- mSystemDir = null;
- mHistoryDir = null;
- initHistoryBuffer();
- }
-
/**
* Used when BatteryStatsImpl object is created from deserialization of a parcel,
- * such as a checkin file.
+ * such as Settings app or checkin file.
+ * @param historyBuffer the history buffer
*/
- private BatteryStatsHistory(Parcel historyBuffer,
- HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
- mHistoryBuffer = historyBuffer;
- mClock = clock;
- mSystemDir = null;
+ public BatteryStatsHistory(Parcel historyBuffer) {
mHistoryDir = null;
- mStepDetailsCalculator = stepDetailsCalculator;
- }
-
- private void initHistoryBuffer() {
- mHistoryBaseTimeMs = 0;
- mLastHistoryElapsedRealtimeMs = 0;
- mTrackRunningHistoryElapsedRealtimeMs = 0;
- mTrackRunningHistoryUptimeMs = 0;
-
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
- mHistoryLastLastWritten.clear();
- mHistoryLastWritten.clear();
- mHistoryTagPool.clear();
- mNextHistoryTagIdx = 0;
- mNumHistoryTagChars = 0;
- mHistoryBufferLastPos = -1;
- mActiveHistoryStates = 0xffffffff;
- mActiveHistoryStates2 = 0xffffffff;
- if (mStepDetailsCalculator != null) {
- mStepDetailsCalculator.clear();
- }
- }
-
- /**
- * Changes the maximum number of history files to be kept.
- */
- public void setMaxHistoryFiles(int maxHistoryFiles) {
- mMaxHistoryFiles = maxHistoryFiles;
- }
-
- /**
- * Changes the maximum size of the history buffer, in bytes.
- */
- public void setMaxHistoryBufferSize(int maxHistoryBufferSize) {
- mMaxHistoryBufferSize = maxHistoryBufferSize;
- }
-
- /**
- * Creates a read-only copy of the battery history. Does not copy the files stored
- * in the system directory, so it is not safe while actively writing history.
- */
- public BatteryStatsHistory copy() {
- // Make a copy of battery history to avoid concurrent modification.
- Parcel historyBuffer = Parcel.obtain();
- historyBuffer.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
- return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null);
+ mHistoryBuffer = historyBuffer;
+ mMaxHistoryFiles = null;
}
- /**
- * Returns true if this instance only supports reading history.
- */
- public boolean isReadOnly() {
- return mActiveFile == null;
+ public File getHistoryDirectory() {
+ return mHistoryDir;
}
/**
@@ -356,13 +221,12 @@ public class BatteryStatsHistory {
/**
* Create history AtomicFile from file number.
- *
* @param num file number.
* @return AtomicFile object.
*/
private AtomicFile getFile(int num) {
return new AtomicFile(
- new File(mHistoryDir, num + FILE_SUFFIX));
+ new File(mHistoryDir, num + FILE_SUFFIX));
}
/**
@@ -370,7 +234,7 @@ public class BatteryStatsHistory {
* create next history file.
*/
public void startNextFile() {
- if (mMaxHistoryFiles == 0) {
+ if (mMaxHistoryFiles == null) {
Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history");
return;
}
@@ -400,7 +264,7 @@ public class BatteryStatsHistory {
// if there are more history files than allowed, delete oldest history files.
// mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and can be updated by GService
// config at run time.
- while (mFileNumbers.size() > mMaxHistoryFiles) {
+ while (mFileNumbers.size() > mMaxHistoryFiles.get()) {
int oldest = mFileNumbers.get(0);
getFile(oldest).delete();
mFileNumbers.remove(0);
@@ -408,43 +272,36 @@ public class BatteryStatsHistory {
}
/**
- * Clear history buffer and delete all existing history files. Active history file start from
- * number 0 again.
+ * Delete all existing history files. Active history file start from number 0 again.
*/
- public void reset() {
- if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!");
+ public void resetAllFiles() {
for (Integer i : mFileNumbers) {
getFile(i).delete();
}
mFileNumbers.clear();
mFileNumbers.add(0);
setActiveFile(0);
-
- initHistoryBuffer();
}
/**
* Start iterating history files and history buffer.
- *
* @return always return true.
*/
- public BatteryStatsHistoryIterator iterate() {
+ public boolean startIteratingHistory() {
mRecordCount = 0;
mCurrentFileIndex = 0;
mCurrentParcel = null;
mCurrentParcelEnd = 0;
mParcelIndex = 0;
- mBatteryStatsHistoryIterator = new BatteryStatsHistoryIterator(this);
- return mBatteryStatsHistoryIterator;
+ return true;
}
/**
* Finish iterating history files and history buffer.
*/
- void finishIteratingHistory() {
+ public void finishIteratingHistory() {
// setDataPosition so mHistoryBuffer Parcel can be written.
mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
- mBatteryStatsHistoryIterator = null;
if (DEBUG) {
Slog.d(TAG, "Battery history records iterated: " + mRecordCount);
}
@@ -454,12 +311,11 @@ public class BatteryStatsHistory {
* When iterating history files and history buffer, always start from the lowest numbered
* history file, when reached the mActiveFile (highest numbered history file), do not read from
* mActiveFile, read from history buffer instead because the buffer has more updated data.
- *
* @param out a history item.
* @return The parcel that has next record. null if finished all history files and history
- * buffer
+ * buffer
*/
- public Parcel getNextParcel(HistoryItem out) {
+ public Parcel getNextParcel(BatteryStats.HistoryItem out) {
if (mRecordCount == 0) {
// reset out if it is the first record.
out.clear();
@@ -467,7 +323,8 @@ public class BatteryStatsHistory {
++mRecordCount;
// First iterate through all records in current parcel.
- if (mCurrentParcel != null) {
+ if (mCurrentParcel != null)
+ {
if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
// There are more records in current parcel.
return mCurrentParcel;
@@ -532,8 +389,7 @@ public class BatteryStatsHistory {
/**
* Read history file into a parcel.
- *
- * @param out the Parcel read into.
+ * @param out the Parcel read into.
* @param file the File to read from.
* @return true if success, false otherwise.
*/
@@ -546,8 +402,8 @@ public class BatteryStatsHistory {
Slog.d(TAG, "readFileToParcel:" + file.getBaseFile().getPath()
+ " duration ms:" + (SystemClock.uptimeMillis() - start));
}
- } catch (Exception e) {
- Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
+ } catch(Exception e) {
+ Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
return false;
}
out.unmarshall(raw, 0, raw.length);
@@ -557,7 +413,6 @@ public class BatteryStatsHistory {
/**
* Skip the header part of history parcel.
- *
* @param p history parcel to skip head.
* @return true if version match, false if not.
*/
@@ -573,68 +428,18 @@ public class BatteryStatsHistory {
}
/**
- * Writes the battery history contents for persistence.
- */
- public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
- out.writeBoolean(inclHistory);
- if (inclHistory) {
- writeToParcel(out);
- }
-
- out.writeInt(mHistoryTagPool.size());
- for (Map.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
- HistoryTag tag = ent.getKey();
- out.writeInt(ent.getValue());
- out.writeString(tag.string);
- out.writeInt(tag.uid);
- }
- }
-
- /**
- * Reads battery history contents from a persisted parcel.
- */
- public void readSummaryFromParcel(Parcel in) {
- boolean inclHistory = in.readBoolean();
- if (inclHistory) {
- readFromParcel(in);
- }
-
- mHistoryTagPool.clear();
- mNextHistoryTagIdx = 0;
- mNumHistoryTagChars = 0;
-
- int numTags = in.readInt();
- for (int i = 0; i < numTags; i++) {
- int idx = in.readInt();
- String str = in.readString();
- int uid = in.readInt();
- HistoryTag tag = new HistoryTag();
- tag.string = str;
- tag.uid = uid;
- tag.poolIdx = idx;
- mHistoryTagPool.put(tag, idx);
- if (idx >= mNextHistoryTagIdx) {
- mNextHistoryTagIdx = idx + 1;
- }
- mNumHistoryTagChars += tag.string.length() + 1;
- }
- }
-
- /**
* Read all history files and serialize into a big Parcel.
* Checkin file calls this method.
*
* @param out the output parcel
*/
public void writeToParcel(Parcel out) {
- writeHistoryBuffer(out);
writeToParcel(out, false /* useBlobs */);
}
/**
* This is for Settings app, when Settings app receives big history parcel, it call
* this method to parse it into list of parcels.
- *
* @param out the output parcel
*/
public void writeToBatteryUsageStatsParcel(Parcel out) {
@@ -645,13 +450,13 @@ public class BatteryStatsHistory {
private void writeToParcel(Parcel out, boolean useBlobs) {
final long start = SystemClock.uptimeMillis();
out.writeInt(mFileNumbers.size() - 1);
- for (int i = 0; i < mFileNumbers.size() - 1; i++) {
+ for(int i = 0; i < mFileNumbers.size() - 1; i++) {
AtomicFile file = getFile(mFileNumbers.get(i));
byte[] raw = new byte[0];
try {
raw = file.readFully();
- } catch (Exception e) {
- Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
+ } catch(Exception e) {
+ Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
}
if (useBlobs) {
out.writeBlob(raw);
@@ -675,55 +480,17 @@ public class BatteryStatsHistory {
Parcel historyBuffer = Parcel.obtain();
historyBuffer.unmarshall(historyBlob, 0, historyBlob.length);
- BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer, null,
- Clock.SYSTEM_CLOCK);
+ BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer);
history.readFromParcel(in, true /* useBlobs */);
return history;
}
/**
- * Read history from a check-in file.
- */
- public boolean readSummary() {
- if (mActiveFile == null) {
- Slog.w(TAG, "readSummary: no history file associated with this instance");
- return false;
- }
-
- Parcel parcel = Parcel.obtain();
- try {
- final long start = SystemClock.uptimeMillis();
- if (mActiveFile.exists()) {
- byte[] raw = mActiveFile.readFully();
- if (raw.length > 0) {
- parcel.unmarshall(raw, 0, raw.length);
- parcel.setDataPosition(0);
- readHistoryBuffer(parcel);
- }
- if (DEBUG) {
- Slog.d(TAG, "read history file::"
- + mActiveFile.getBaseFile().getPath()
- + " bytes:" + raw.length + " took ms:" + (SystemClock.uptimeMillis()
- - start));
- }
- }
- } catch (Exception e) {
- Slog.e(TAG, "Error reading battery history", e);
- reset();
- return false;
- } finally {
- parcel.recycle();
- }
- return true;
- }
-
- /**
* This is for the check-in file, which has all history files embedded.
*
* @param in the input parcel.
*/
public void readFromParcel(Parcel in) {
- readHistoryBuffer(in);
readFromParcel(in, false /* useBlobs */);
}
@@ -731,7 +498,7 @@ public class BatteryStatsHistory {
final long start = SystemClock.uptimeMillis();
mHistoryParcels = new ArrayList<>();
final int count = in.readInt();
- for (int i = 0; i < count; i++) {
+ for(int i = 0; i < count; i++) {
byte[] temp = useBlobs ? in.readBlob() : in.createByteArray();
if (temp == null || temp.length == 0) {
continue;
@@ -754,12 +521,10 @@ public class BatteryStatsHistory {
return stats.getAvailableBytes() > MIN_FREE_SPACE;
}
- @VisibleForTesting
public List<Integer> getFilesNumbers() {
return mFileNumbers;
}
- @VisibleForTesting
public AtomicFile getActiveFile() {
return mActiveFile;
}
@@ -769,972 +534,15 @@ public class BatteryStatsHistory {
*/
public int getHistoryUsedSize() {
int ret = 0;
- for (int i = 0; i < mFileNumbers.size() - 1; i++) {
+ for(int i = 0; i < mFileNumbers.size() - 1; i++) {
ret += getFile(mFileNumbers.get(i)).getBaseFile().length();
}
ret += mHistoryBuffer.dataSize();
if (mHistoryParcels != null) {
- for (int i = 0; i < mHistoryParcels.size(); i++) {
+ for(int i = 0; i < mHistoryParcels.size(); i++) {
ret += mHistoryParcels.get(i).dataSize();
}
}
return ret;
}
-
- /**
- * Enables/disables recording of history. When disabled, all "record*" calls are a no-op.
- */
- public void setHistoryRecordingEnabled(boolean enabled) {
- mRecordingHistory = enabled;
- }
-
- /**
- * Returns true if history recording is enabled.
- */
- public boolean isRecordingHistory() {
- return mRecordingHistory;
- }
-
- /**
- * Forces history recording regardless of charging state.
- */
- @VisibleForTesting
- public void forceRecordAllHistory() {
- mHaveBatteryLevel = true;
- mRecordingHistory = true;
- }
-
- /**
- * Starts a history buffer by recording the current wall-clock time.
- */
- public void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
- boolean reset) {
- mRecordingHistory = true;
- mHistoryCur.currentTime = mClock.currentTimeMillis();
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur,
- reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME);
- mHistoryCur.currentTime = 0;
- }
-
- /**
- * Prepares to continue recording after restoring previous history from persistent storage.
- */
- public void continueRecordingHistory() {
- if (mHistoryBuffer.dataPosition() <= 0 && mFileNumbers.size() <= 1) {
- return;
- }
-
- mRecordingHistory = true;
- final long elapsedRealtimeMs = mClock.elapsedRealtime();
- final long uptimeMs = mClock.uptimeMillis();
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START);
- startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
- }
-
- /**
- * Notes the current battery state to be reflected in the next written history item.
- */
- public void setBatteryState(boolean charging, int status, int level, int chargeUah) {
- mHaveBatteryLevel = true;
- setChargingState(charging);
- mHistoryCur.batteryStatus = (byte) status;
- mHistoryCur.batteryLevel = (byte) level;
- mHistoryCur.batteryChargeUah = chargeUah;
- }
-
- /**
- * Notes the current battery state to be reflected in the next written history item.
- */
- public void setBatteryState(int status, int level, int health, int plugType, int temperature,
- int voltageMv, int chargeUah) {
- mHaveBatteryLevel = true;
- mHistoryCur.batteryStatus = (byte) status;
- mHistoryCur.batteryLevel = (byte) level;
- mHistoryCur.batteryHealth = (byte) health;
- mHistoryCur.batteryPlugType = (byte) plugType;
- mHistoryCur.batteryTemperature = (short) temperature;
- mHistoryCur.batteryVoltage = (char) voltageMv;
- mHistoryCur.batteryChargeUah = chargeUah;
- }
-
- /**
- * Notes the current power plugged-in state to be reflected in the next written history item.
- */
- public void setPluggedInState(boolean pluggedIn) {
- if (pluggedIn) {
- mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- } else {
- mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
- }
- }
-
- /**
- * Notes the current battery charging state to be reflected in the next written history item.
- */
- public void setChargingState(boolean charging) {
- if (charging) {
- mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
- } else {
- mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG;
- }
- }
-
- /**
- * Records a history event with the given code, name and UID.
- */
- public void recordEvent(long elapsedRealtimeMs, long uptimeMs, int code, String name,
- int uid) {
- mHistoryCur.eventCode = code;
- mHistoryCur.eventTag = mHistoryCur.localEventTag;
- mHistoryCur.eventTag.string = name;
- mHistoryCur.eventTag.uid = uid;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a time change event.
- */
- public void recordCurrentTimeChange(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) {
- if (!mRecordingHistory) {
- return;
- }
-
- mHistoryCur.currentTime = currentTimeMs;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur,
- HistoryItem.CMD_CURRENT_TIME);
- mHistoryCur.currentTime = 0;
- }
-
- /**
- * Records a system shutdown event.
- */
- public void recordShutdownEvent(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) {
- if (!mRecordingHistory) {
- return;
- }
-
- mHistoryCur.currentTime = currentTimeMs;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN);
- mHistoryCur.currentTime = 0;
- }
-
- /**
- * Records a battery state change event.
- */
- public void recordBatteryState(long elapsedRealtimeMs, long uptimeMs, int batteryLevel,
- boolean isPlugged) {
- mHistoryCur.batteryLevel = (byte) batteryLevel;
- setPluggedInState(isPlugged);
- if (DEBUG) {
- Slog.v(TAG, "Battery unplugged to: "
- + Integer.toHexString(mHistoryCur.states));
- }
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a history item with the amount of charge consumed by WiFi. Used on certain devices
- * equipped with on-device power metering.
- */
- public void recordWifiConsumedCharge(long elapsedRealtimeMs, long uptimeMs,
- double monitoredRailChargeMah) {
- mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a wakelock start event.
- */
- public void recordWakelockStartEvent(long elapsedRealtimeMs, long uptimeMs, String historyName,
- int uid) {
- mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName;
- mHistoryCur.wakelockTag.uid = uid;
- recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG);
- }
-
- /**
- * Updates the previous history event with a wakelock name and UID.
- */
- public boolean maybeUpdateWakelockTag(long elapsedRealtimeMs, long uptimeMs, String historyName,
- int uid) {
- if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) {
- return false;
- }
- if (mHistoryLastWritten.wakelockTag != null) {
- // We'll try to update the last tag.
- mHistoryLastWritten.wakelockTag = null;
- mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName;
- mHistoryCur.wakelockTag.uid = uid;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
- return true;
- }
-
- /**
- * Records an event when some state flag changes to true.
- */
- public void recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
- mHistoryCur.states |= stateFlags;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records an event when some state flag changes to false.
- */
- public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
- mHistoryCur.states &= ~stateFlags;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records an event when some state flags change to true and some to false.
- */
- public void recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags,
- int stateStopFlags) {
- mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records an event when some state2 flag changes to true.
- */
- public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
- mHistoryCur.states2 |= stateFlags;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records an event when some state2 flag changes to false.
- */
- public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
- mHistoryCur.states2 &= ~stateFlags;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records an wakeup event.
- */
- public void recordWakeupEvent(long elapsedRealtimeMs, long uptimeMs, String reason) {
- mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
- mHistoryCur.wakeReasonTag.string = reason;
- mHistoryCur.wakeReasonTag.uid = 0;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a screen brightness change event.
- */
- public void recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs,
- int brightnessBin) {
- mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK)
- | (brightnessBin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a GNSS signal level change event.
- */
- public void recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs,
- int signalLevel) {
- mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
- | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a device idle mode change event.
- */
- public void recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode) {
- mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
- | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a telephony state change event.
- */
- public void recordPhoneStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int addStateFlag,
- int removeStateFlag, int state, int signalStrength) {
- mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag;
- if (state != -1) {
- mHistoryCur.states =
- (mHistoryCur.states & ~HistoryItem.STATE_PHONE_STATE_MASK)
- | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
- }
- if (signalStrength != -1) {
- mHistoryCur.states =
- (mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
- | (signalStrength << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
- }
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a data connection type change event.
- */
- public void recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs,
- int dataConnectionType) {
- mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_DATA_CONNECTION_MASK)
- | (dataConnectionType << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a WiFi supplicant state change event.
- */
- public void recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs,
- int supplState) {
- mHistoryCur.states2 =
- (mHistoryCur.states2 & ~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
- | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Records a WiFi signal strength change event.
- */
- public void recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs,
- int strengthBin) {
- mHistoryCur.states2 =
- (mHistoryCur.states2 & ~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
- | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
- * Writes the current history item to history.
- */
- public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) {
- if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
- final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
- final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs;
- if (diffUptimeMs < (diffElapsedMs - 20)) {
- final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs);
- mHistoryAddTmp.setTo(mHistoryLastWritten);
- mHistoryAddTmp.wakelockTag = null;
- mHistoryAddTmp.wakeReasonTag = null;
- mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
- mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
- writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
- }
- }
- mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
- mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs;
- mTrackRunningHistoryUptimeMs = uptimeMs;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur);
- }
-
- private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
- if (!mHaveBatteryLevel || !mRecordingHistory) {
- return;
- }
-
- final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
- final int diffStates = mHistoryLastWritten.states ^ (cur.states & mActiveHistoryStates);
- final int diffStates2 = mHistoryLastWritten.states2 ^ (cur.states2 & mActiveHistoryStates2);
- final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states;
- final int lastDiffStates2 = mHistoryLastWritten.states2 ^ mHistoryLastLastWritten.states2;
- if (DEBUG) {
- Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff="
- + Integer.toHexString(diffStates) + " lastDiff="
- + Integer.toHexString(lastDiffStates) + " diff2="
- + Integer.toHexString(diffStates2) + " lastDiff2="
- + Integer.toHexString(lastDiffStates2));
- }
- if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
- && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
- && (diffStates2 & lastDiffStates2) == 0
- && (!mHistoryLastWritten.tagsFirstOccurrence && !cur.tagsFirstOccurrence)
- && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
- && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
- && mHistoryLastWritten.stepDetails == null
- && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
- || cur.eventCode == HistoryItem.EVENT_NONE)
- && mHistoryLastWritten.batteryLevel == cur.batteryLevel
- && mHistoryLastWritten.batteryStatus == cur.batteryStatus
- && mHistoryLastWritten.batteryHealth == cur.batteryHealth
- && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
- && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
- && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
- // We can merge this new change in with the last one. Merging is
- // allowed as long as only the states have changed, and within those states
- // as long as no bit has changed both between now and the last entry, as
- // well as the last entry and the one before it (so we capture any toggles).
- if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
- mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
- mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
- mHistoryBufferLastPos = -1;
- elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
- // If the last written history had a wakelock tag, we need to retain it.
- // Note that the condition above made sure that we aren't in a case where
- // both it and the current history item have a wakelock tag.
- if (mHistoryLastWritten.wakelockTag != null) {
- cur.wakelockTag = cur.localWakelockTag;
- cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
- }
- // If the last written history had a wake reason tag, we need to retain it.
- // Note that the condition above made sure that we aren't in a case where
- // both it and the current history item have a wakelock tag.
- if (mHistoryLastWritten.wakeReasonTag != null) {
- cur.wakeReasonTag = cur.localWakeReasonTag;
- cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
- }
- // If the last written history had an event, we need to retain it.
- // Note that the condition above made sure that we aren't in a case where
- // both it and the current history item have an event.
- if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
- cur.eventCode = mHistoryLastWritten.eventCode;
- cur.eventTag = cur.localEventTag;
- cur.eventTag.setTo(mHistoryLastWritten.eventTag);
- }
- mHistoryLastWritten.setTo(mHistoryLastLastWritten);
- }
- final int dataSize = mHistoryBuffer.dataSize();
-
- if (dataSize >= mMaxHistoryBufferSize) {
- if (mMaxHistoryBufferSize == 0) {
- Slog.wtf(TAG, "mMaxHistoryBufferSize should not be zero when writing history");
- mMaxHistoryBufferSize = 1024;
- }
-
- //open a new history file.
- final long start = SystemClock.uptimeMillis();
- writeHistory();
- if (DEBUG) {
- Slog.d(TAG, "addHistoryBufferLocked writeHistory took ms:"
- + (SystemClock.uptimeMillis() - start));
- }
- startNextFile();
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
- mHistoryBufferLastPos = -1;
- mHistoryLastWritten.clear();
- mHistoryLastLastWritten.clear();
-
- // Mark every entry in the pool with a flag indicating that the tag
- // has not yet been encountered while writing the current history buffer.
- for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) {
- entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
- }
- // Make a copy of mHistoryCur.
- HistoryItem copy = new HistoryItem();
- copy.setTo(cur);
- // startRecordingHistory will reset mHistoryCur.
- startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
- // Add the copy into history buffer.
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_UPDATE);
- return;
- }
-
- if (dataSize == 0) {
- // The history is currently empty; we need it to start with a time stamp.
- cur.currentTime = mClock.currentTimeMillis();
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_RESET);
- }
- writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE);
- }
-
- private void writeHistoryItem(long elapsedRealtimeMs,
- @SuppressWarnings("UnusedVariable") long uptimeMs, HistoryItem cur, byte cmd) {
- if (mBatteryStatsHistoryIterator != null) {
- throw new IllegalStateException("Can't do this while iterating history!");
- }
- mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
- mHistoryLastLastWritten.setTo(mHistoryLastWritten);
- final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
- mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
- mHistoryLastWritten.tagsFirstOccurrence = hasTags;
- mHistoryLastWritten.states &= mActiveHistoryStates;
- mHistoryLastWritten.states2 &= mActiveHistoryStates2;
- writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
- mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
- cur.wakelockTag = null;
- cur.wakeReasonTag = null;
- cur.eventCode = HistoryItem.EVENT_NONE;
- cur.eventTag = null;
- cur.tagsFirstOccurrence = false;
- if (DEBUG) {
- Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
- + " now " + mHistoryBuffer.dataPosition()
- + " size is now " + mHistoryBuffer.dataSize());
- }
- }
-
- /*
- The history delta format uses flags to denote further data in subsequent ints in the parcel.
-
- There is always the first token, which may contain the delta time, or an indicator of
- the length of the time (int or long) following this token.
-
- First token: always present,
- 31 23 15 7 0
- █M|L|K|J|I|H|G|F█E|D|C|B|A|T|T|T█T|T|T|T|T|T|T|T█T|T|T|T|T|T|T|T█
-
- T: the delta time if it is <= 0x7fffd. Otherwise 0x7fffe indicates an int immediately
- follows containing the time, and 0x7ffff indicates a long immediately follows with the
- delta time.
- A: battery level changed and an int follows with battery data.
- B: state changed and an int follows with state change data.
- C: state2 has changed and an int follows with state2 change data.
- D: wakelock/wakereason has changed and an wakelock/wakereason struct follows.
- E: event data has changed and an event struct follows.
- F: battery charge in coulombs has changed and an int with the charge follows.
- G: state flag denoting that the mobile radio was active.
- H: state flag denoting that the wifi radio was active.
- I: state flag denoting that a wifi scan occurred.
- J: state flag denoting that a wifi full lock was held.
- K: state flag denoting that the gps was on.
- L: state flag denoting that a wakelock was held.
- M: state flag denoting that the cpu was running.
-
- Time int/long: if T in the first token is 0x7ffff or 0x7fffe, then an int or long follows
- with the time delta.
-
- Battery level int: if A in the first token is set,
- 31 23 15 7 0
- █L|L|L|L|L|L|L|T█T|T|T|T|T|T|T|T█T|V|V|V|V|V|V|V█V|V|V|V|V|V|V|D█
-
- D: indicates that extra history details follow.
- V: the battery voltage.
- T: the battery temperature.
- L: the battery level (out of 100).
-
- State change int: if B in the first token is set,
- 31 23 15 7 0
- █S|S|S|H|H|H|P|P█F|E|D|C|B| | |A█ | | | | | | | █ | | | | | | | █
-
- A: wifi multicast was on.
- B: battery was plugged in.
- C: screen was on.
- D: phone was scanning for signal.
- E: audio was on.
- F: a sensor was active.
-
- State2 change int: if C in the first token is set,
- 31 23 15 7 0
- █M|L|K|J|I|H|H|G█F|E|D|C| | | | █ | | | | | | | █ |B|B|B|A|A|A|A█
-
- A: 4 bits indicating the wifi supplicant state: {@link BatteryStats#WIFI_SUPPL_STATE_NAMES}.
- B: 3 bits indicating the wifi signal strength: 0, 1, 2, 3, 4.
- C: a bluetooth scan was active.
- D: the camera was active.
- E: bluetooth was on.
- F: a phone call was active.
- G: the device was charging.
- H: 2 bits indicating the device-idle (doze) state: off, light, full
- I: the flashlight was on.
- J: wifi was on.
- K: wifi was running.
- L: video was playing.
- M: power save mode was on.
-
- Wakelock/wakereason struct: if D in the first token is set,
- Event struct: if E in the first token is set,
- History step details struct: if D in the battery level int is set,
-
- Battery charge int: if F in the first token is set, an int representing the battery charge
- in coulombs follows.
- */
- /**
- * Writes the delta between the previous and current history items into history buffer.
- */
- public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
- if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
- dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS);
- cur.writeToParcel(dest, 0);
- return;
- }
-
- final long deltaTime = cur.time - last.time;
- final int lastBatteryLevelInt = buildBatteryLevelInt(last);
- final int lastStateInt = buildStateInt(last);
-
- int deltaTimeToken;
- if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
- deltaTimeToken = BatteryStatsHistory.DELTA_TIME_LONG;
- } else if (deltaTime >= BatteryStatsHistory.DELTA_TIME_ABS) {
- deltaTimeToken = BatteryStatsHistory.DELTA_TIME_INT;
- } else {
- deltaTimeToken = (int) deltaTime;
- }
- int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK);
- final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
- ? BatteryStatsHistory.BATTERY_DELTA_LEVEL_FLAG : 0;
- mLastHistoryStepLevel = cur.batteryLevel;
- final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
- final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
- if (batteryLevelIntChanged) {
- firstToken |= BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG;
- }
- final int stateInt = buildStateInt(cur);
- final boolean stateIntChanged = stateInt != lastStateInt;
- if (stateIntChanged) {
- firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
- }
- final boolean state2IntChanged = cur.states2 != last.states2;
- if (state2IntChanged) {
- firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG;
- }
- if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
- firstToken |= BatteryStatsHistory.DELTA_WAKELOCK_FLAG;
- }
- if (cur.eventCode != HistoryItem.EVENT_NONE) {
- firstToken |= BatteryStatsHistory.DELTA_EVENT_FLAG;
- }
-
- final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah;
- if (batteryChargeChanged) {
- firstToken |= BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG;
- }
- dest.writeInt(firstToken);
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
- + " deltaTime=" + deltaTime);
- }
-
- if (deltaTimeToken >= BatteryStatsHistory.DELTA_TIME_INT) {
- if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int) deltaTime);
- dest.writeInt((int) deltaTime);
- } else {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
- dest.writeLong(deltaTime);
- }
- }
- if (batteryLevelIntChanged) {
- dest.writeInt(batteryLevelInt);
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
- + Integer.toHexString(batteryLevelInt)
- + " batteryLevel=" + cur.batteryLevel
- + " batteryTemp=" + cur.batteryTemperature
- + " batteryVolt=" + (int) cur.batteryVoltage);
- }
- }
- if (stateIntChanged) {
- dest.writeInt(stateInt);
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: stateToken=0x"
- + Integer.toHexString(stateInt)
- + " batteryStatus=" + cur.batteryStatus
- + " batteryHealth=" + cur.batteryHealth
- + " batteryPlugType=" + cur.batteryPlugType
- + " states=0x" + Integer.toHexString(cur.states));
- }
- }
- if (state2IntChanged) {
- dest.writeInt(cur.states2);
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: states2=0x"
- + Integer.toHexString(cur.states2));
- }
- }
- if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
- int wakeLockIndex;
- int wakeReasonIndex;
- if (cur.wakelockTag != null) {
- wakeLockIndex = writeHistoryTag(cur.wakelockTag);
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
- + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
- }
- } else {
- wakeLockIndex = 0xffff;
- }
- if (cur.wakeReasonTag != null) {
- wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
- + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
- }
- } else {
- wakeReasonIndex = 0xffff;
- }
- dest.writeInt((wakeReasonIndex << 16) | wakeLockIndex);
- if (cur.wakelockTag != null
- && (wakeLockIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- cur.wakelockTag.writeToParcel(dest, 0);
- cur.tagsFirstOccurrence = true;
- }
- if (cur.wakeReasonTag != null
- && (wakeReasonIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- cur.wakeReasonTag.writeToParcel(dest, 0);
- cur.tagsFirstOccurrence = true;
- }
- }
- if (cur.eventCode != HistoryItem.EVENT_NONE) {
- final int index = writeHistoryTag(cur.eventTag);
- final int codeAndIndex = (cur.eventCode & 0xffff) | (index << 16);
- dest.writeInt(codeAndIndex);
- if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- cur.eventTag.writeToParcel(dest, 0);
- cur.tagsFirstOccurrence = true;
- }
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#"
- + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
- + cur.eventTag.string);
- }
- }
-
- cur.stepDetails = mStepDetailsCalculator.getHistoryStepDetails();
- if (includeStepDetails != 0) {
- cur.stepDetails.writeToParcel(dest);
- }
-
- if (batteryChargeChanged) {
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah);
- dest.writeInt(cur.batteryChargeUah);
- }
- dest.writeDouble(cur.modemRailChargeMah);
- dest.writeDouble(cur.wifiRailChargeMah);
- }
-
- private int buildBatteryLevelInt(HistoryItem h) {
- return ((((int) h.batteryLevel) << 25) & 0xfe000000)
- | ((((int) h.batteryTemperature) << 15) & 0x01ff8000)
- | ((((int) h.batteryVoltage) << 1) & 0x00007ffe);
- }
-
- private int buildStateInt(HistoryItem h) {
- int plugType = 0;
- if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_AC) != 0) {
- plugType = 1;
- } else if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_USB) != 0) {
- plugType = 2;
- } else if ((h.batteryPlugType & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) {
- plugType = 3;
- }
- return ((h.batteryStatus & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK)
- << BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT)
- | ((h.batteryHealth & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK)
- << BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT)
- | ((plugType & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK)
- << BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT)
- | (h.states & (~BatteryStatsHistory.STATE_BATTERY_MASK));
- }
-
- /**
- * Returns the index for the specified tag. If this is the first time the tag is encountered
- * while writing the current history buffer, the method returns
- * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code>
- */
- private int writeHistoryTag(HistoryTag tag) {
- if (tag.string == null) {
- Slog.wtfStack(TAG, "writeHistoryTag called with null name");
- }
-
- final int stringLength = tag.string.length();
- if (stringLength > MAX_HISTORY_TAG_STRING_LENGTH) {
- Slog.e(TAG, "Long battery history tag: " + tag.string);
- tag.string = tag.string.substring(0, MAX_HISTORY_TAG_STRING_LENGTH);
- }
-
- Integer idxObj = mHistoryTagPool.get(tag);
- int idx;
- if (idxObj != null) {
- idx = idxObj;
- if ((idx & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
- mHistoryTagPool.put(tag, idx & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
- }
- return idx;
- } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) {
- idx = mNextHistoryTagIdx;
- HistoryTag key = new HistoryTag();
- key.setTo(tag);
- tag.poolIdx = idx;
- mHistoryTagPool.put(key, idx);
- mNextHistoryTagIdx++;
-
- mNumHistoryTagChars += stringLength + 1;
- if (mHistoryTags != null) {
- mHistoryTags.put(idx, key);
- }
- return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
- } else {
- // Tag pool overflow: include the tag itself in the parcel
- return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
- }
- }
-
- /**
- * Don't allow any more batching in to the current history event.
- */
- public void commitCurrentHistoryBatchLocked() {
- mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
- }
-
- /**
- * Saves the accumulated history buffer in the active file, see {@link #getActiveFile()} .
- */
- public void writeHistory() {
- if (mActiveFile == null) {
- Slog.w(TAG, "writeHistory: no history file associated with this instance");
- return;
- }
-
- Parcel p = Parcel.obtain();
- try {
- final long start = SystemClock.uptimeMillis();
- writeHistoryBuffer(p);
- if (DEBUG) {
- Slog.d(TAG, "writeHistoryBuffer duration ms:"
- + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
- }
- writeParcelToFileLocked(p, mActiveFile);
- } finally {
- p.recycle();
- }
- }
-
- /**
- * Reads history buffer from a persisted Parcel.
- */
- public void readHistoryBuffer(Parcel in) throws ParcelFormatException {
- final int version = in.readInt();
- if (version != BatteryStatsHistory.VERSION) {
- Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
- + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats");
- return;
- }
-
- final long historyBaseTime = in.readLong();
-
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
-
- int bufSize = in.readInt();
- int curPos = in.dataPosition();
- if (bufSize >= (mMaxHistoryBufferSize * 100)) {
- throw new ParcelFormatException(
- "File corrupt: history data buffer too large " + bufSize);
- } else if ((bufSize & ~3) != bufSize) {
- throw new ParcelFormatException(
- "File corrupt: history data buffer not aligned " + bufSize);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
- + " bytes at " + curPos);
- }
- mHistoryBuffer.appendFrom(in, curPos, bufSize);
- in.setDataPosition(curPos + bufSize);
- }
-
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** OLD mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- mHistoryBaseTimeMs = historyBaseTime;
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** NEW mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
-
- // We are just arbitrarily going to insert 1 minute from the sample of
- // the last run until samples in this run.
- if (mHistoryBaseTimeMs > 0) {
- long oldnow = mClock.elapsedRealtime();
- mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- }
- }
-
- private void writeHistoryBuffer(Parcel out) {
- if (DEBUG) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("****************** WRITING mHistoryBaseTimeMs: ");
- TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
- sb.append(" mLastHistoryElapsedRealtimeMs: ");
- TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
- Slog.i(TAG, sb.toString());
- }
- out.writeInt(BatteryStatsHistory.VERSION);
- out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
- out.writeInt(mHistoryBuffer.dataSize());
- if (DEBUG) {
- Slog.i(TAG, "***************** WRITING HISTORY: "
- + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
- }
- out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
- }
-
- private void writeParcelToFileLocked(Parcel p, AtomicFile file) {
- FileOutputStream fos = null;
- mWriteLock.lock();
- try {
- final long startTimeMs = SystemClock.uptimeMillis();
- fos = file.startWrite();
- fos.write(p.marshall());
- fos.flush();
- file.finishWrite(fos);
- if (DEBUG) {
- Slog.d(TAG, "writeParcelToFileLocked file:" + file.getBaseFile().getPath()
- + " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs)
- + " bytes:" + p.dataSize());
- }
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
- "batterystats", SystemClock.uptimeMillis() - startTimeMs);
- } catch (IOException e) {
- Slog.w(TAG, "Error writing battery statistics", e);
- file.failWrite(fos);
- } finally {
- mWriteLock.unlock();
- }
- }
-
- /**
- * Returns the total number of history tags in the tag pool.
- */
- public int getHistoryStringPoolSize() {
- return mHistoryTagPool.size();
- }
-
- /**
- * Returns the total number of bytes occupied by the history tag pool.
- */
- public int getHistoryStringPoolBytes() {
- return mNumHistoryTagChars;
- }
-
- /**
- * Returns the string held by the requested history tag.
- */
- public String getHistoryTagPoolString(int index) {
- ensureHistoryTagArray();
- HistoryTag historyTag = mHistoryTags.get(index);
- return historyTag != null ? historyTag.string : null;
- }
-
- /**
- * Returns the UID held by the requested history tag.
- */
- public int getHistoryTagPoolUid(int index) {
- ensureHistoryTagArray();
- HistoryTag historyTag = mHistoryTags.get(index);
- return historyTag != null ? historyTag.uid : Process.INVALID_UID;
- }
-
- private void ensureHistoryTagArray() {
- if (mHistoryTags != null) {
- return;
- }
-
- mHistoryTags = new SparseArray<>(mHistoryTagPool.size());
- for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) {
- mHistoryTags.put(entry.getValue() & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG,
- entry.getKey());
- }
- }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 1bf878cb9119..de8b414c4b78 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -36,6 +36,7 @@ public class BatteryStatsHistoryIterator {
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) {
mBatteryStatsHistory = history;
+ mBatteryStatsHistory.startIteratingHistory();
}
/**
@@ -230,11 +231,4 @@ public class BatteryStatsHistoryIterator {
out.batteryTemperature = (short) ((batteryLevelInt & 0x01ff8000) >>> 15);
out.batteryVoltage = (char) ((batteryLevelInt & 0x00007ffe) >>> 1);
}
-
- /**
- * Should be called when iteration is complete.
- */
- public void close() {
- mBatteryStatsHistory.finishIteratingHistory();
- }
}
diff --git a/core/java/com/android/internal/util/FastDataOutput.java b/core/java/com/android/internal/util/FastDataOutput.java
index c9e8f8f08229..5b6075ec54ce 100644
--- a/core/java/com/android/internal/util/FastDataOutput.java
+++ b/core/java/com/android/internal/util/FastDataOutput.java
@@ -59,7 +59,7 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
/**
* Values that have been "interned" by {@link #writeInternedUTF(String)}.
*/
- private final HashMap<String, Short> mStringRefs = new HashMap<>();
+ private final HashMap<String, Integer> mStringRefs = new HashMap<>();
/**
* @deprecated callers must specify {@code use4ByteSequence} so they make a
@@ -256,7 +256,7 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
* @see FastDataInput#readInternedUTF()
*/
public void writeInternedUTF(@NonNull String s) throws IOException {
- Short ref = mStringRefs.get(s);
+ Integer ref = mStringRefs.get(s);
if (ref != null) {
writeShort(ref);
} else {
@@ -265,7 +265,7 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
// We can only safely intern when we have remaining values; if we're
// full we at least sent the string value above
- ref = (short) mStringRefs.size();
+ ref = mStringRefs.size();
if (ref < MAX_UNSIGNED_SHORT) {
mStringRefs.put(s, ref);
}
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index 95a4e1238e06..bc729f10d74c 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -1475,7 +1475,6 @@ public final class LocalFloatingToolbarPopup implements FloatingToolbarPopup {
contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentContainer.setTag(FloatingToolbar.FLOATING_TOOLBAR_TAG);
- contentContainer.setContentDescription(FloatingToolbar.FLOATING_TOOLBAR_TAG);
contentContainer.setClipToOutline(true);
return contentContainer;
}
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 671e63493323..7f50204fb842 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -49,7 +49,7 @@ per-file android_hardware_SensorManager* = arthuri@google.com, bduddie@google.co
per-file *Zygote* = file:/ZYGOTE_OWNERS
per-file core_jni_helpers.* = file:/ZYGOTE_OWNERS
per-file fd_utils.* = file:/ZYGOTE_OWNERS
-per-file Android.bp = file:platform/build/soong:/OWNERS
+per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file android_animation_* = file:/core/java/android/animation/OWNERS
per-file android_app_admin_* = file:/core/java/android/app/admin/OWNERS
per-file android_hardware_Usb* = file:/services/usb/OWNERS
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index d8522658d747..17cfbfc63f65 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -720,19 +720,22 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, jint target, jint level,
jint internalformat, jobject bitmapObj, jint type, jint border)
{
graphics::Bitmap bitmap(env, bitmapObj);
- AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
+ if (bitmap.isValid() && bitmap.getPixels() != nullptr) {
+ AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
- if (internalformat < 0) {
- internalformat = getInternalFormat(bitmapInfo.format);
- }
- if (type < 0) {
- type = getType(bitmapInfo.format);
- }
+ if (internalformat < 0) {
+ internalformat = getInternalFormat(bitmapInfo.format);
+ }
+ if (type < 0) {
+ type = getType(bitmapInfo.format);
+ }
- if (checkInternalFormat(bitmapInfo.format, internalformat, type)) {
- glTexImage2D(target, level, internalformat, bitmapInfo.width, bitmapInfo.height, border,
- getPixelFormatFromInternalFormat(internalformat), type, bitmap.getPixels());
- return 0;
+ if (checkInternalFormat(bitmapInfo.format, internalformat, type)) {
+ glTexImage2D(target, level, internalformat, bitmapInfo.width, bitmapInfo.height, border,
+ getPixelFormatFromInternalFormat(internalformat), type,
+ bitmap.getPixels());
+ return 0;
+ }
}
return -1;
}
@@ -741,19 +744,21 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz, jint target, jint leve
jint xoffset, jint yoffset, jobject bitmapObj, jint format, jint type)
{
graphics::Bitmap bitmap(env, bitmapObj);
- AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
-
- int internalFormat = getInternalFormat(bitmapInfo.format);
- if (format < 0) {
- format = getPixelFormatFromInternalFormat(internalFormat);
- if (format == GL_PALETTE8_RGBA8_OES)
- return -1; // glCompressedTexSubImage2D() not supported
- }
+ if (bitmap.isValid() && bitmap.getPixels() != nullptr) {
+ AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
+
+ int internalFormat = getInternalFormat(bitmapInfo.format);
+ if (format < 0) {
+ format = getPixelFormatFromInternalFormat(internalFormat);
+ if (format == GL_PALETTE8_RGBA8_OES)
+ return -1; // glCompressedTexSubImage2D() not supported
+ }
- if (checkInternalFormat(bitmapInfo.format, internalFormat, type)) {
- glTexSubImage2D(target, level, xoffset, yoffset, bitmapInfo.width, bitmapInfo.height,
- format, type, bitmap.getPixels());
- return 0;
+ if (checkInternalFormat(bitmapInfo.format, internalFormat, type)) {
+ glTexSubImage2D(target, level, xoffset, yoffset, bitmapInfo.width, bitmapInfo.height,
+ format, type, bitmap.getPixels());
+ return 0;
+ }
}
return -1;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9ae16304b6c8..bc299fd0f1fe 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -820,14 +820,6 @@ static void nativeSetStretchEffect(JNIEnv* env, jclass clazz, jlong transactionO
transaction->setStretchEffect(ctrl, stretch);
}
-static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj,
- jlong nativeObject, jint w, jint h) {
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-
- SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- transaction->setSize(ctrl, w, h);
-}
-
static void nativeSetFlags(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint flags, jint mask) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2121,6 +2113,20 @@ static jint nativeGetLayerId(JNIEnv* env, jclass clazz, jlong nativeSurfaceContr
return surface->getLayerId();
}
+static void nativeSetDefaultApplyToken(JNIEnv* env, jclass clazz, jobject applyToken) {
+ sp<IBinder> token(ibinderForJavaObject(env, applyToken));
+ if (token == nullptr) {
+ ALOGE("Null apply token provided.");
+ return;
+ }
+ SurfaceComposerClient::Transaction::setDefaultApplyToken(token);
+}
+
+static jobject nativeGetDefaultApplyToken(JNIEnv* env, jclass clazz) {
+ sp<IBinder> token = SurfaceComposerClient::Transaction::getDefaultApplyToken();
+ return javaObjectForIBinder(env, token);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -2163,8 +2169,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetPosition },
{"nativeSetScale", "(JJFF)V",
(void*)nativeSetScale },
- {"nativeSetSize", "(JJII)V",
- (void*)nativeSetSize },
{"nativeSetTransparentRegionHint", "(JJLandroid/graphics/Region;)V",
(void*)nativeSetTransparentRegionHint },
{"nativeSetDamageRegion", "(JJLandroid/graphics/Region;)V",
@@ -2343,6 +2347,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*) nativeSanitize },
{"nativeSetDestinationFrame", "(JJIIII)V",
(void*)nativeSetDestinationFrame },
+ {"nativeSetDefaultApplyToken", "(Landroid/os/IBinder;)V",
+ (void*)nativeSetDefaultApplyToken },
+ {"nativeGetDefaultApplyToken", "()Landroid/os/IBinder;",
+ (void*)nativeGetDefaultApplyToken },
// clang-format on
};
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index ca0373773577..776a35d15ef0 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -16,6 +16,7 @@
*/
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/floating_popup_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="0dp"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b59cf7fd3bcb..116b15051e7d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2392,9 +2392,6 @@
<!-- The list of supported dream complications -->
<integer-array name="config_supportedDreamComplications">
</integer-array>
- <!-- The list of dream complications which should be enabled by default -->
- <integer-array name="config_dreamComplicationsEnabledByDefault">
- </integer-array>
<!-- Are we allowed to dream while not plugged in? -->
<bool name="config_dreamsEnabledOnBattery">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b7da6ae31ce3..7a2c866375d2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5422,8 +5422,10 @@
<!-- Hint text in a search edit box (used to filter long language / country lists) [CHAR LIMIT=25] -->
<string name="search_language_hint">Type language name</string>
- <!-- List section subheader for the language picker, containing a list of suggested languages determined by the default region [CHAR LIMIT=30] -->
+ <!-- List section subheader for the language picker, containing a list of suggested languages [CHAR LIMIT=30] -->
<string name="language_picker_section_suggested">Suggested</string>
+ <!-- "List section subheader for the language picker, containing a list of suggested regions available for that language [CHAR LIMIT=30] -->
+ <string name="language_picker_regions_section_suggested">Suggested</string>
<!-- List section subheader for the language picker, containing a list of suggested languages determined by the default region [CHAR LIMIT=30] -->
<string name="language_picker_section_suggested_bilingual">Suggested languages</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d60fc209ebc1..3afa6efab2a6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2228,7 +2228,6 @@
<java-symbol type="string" name="config_dreamsDefaultComponent" />
<java-symbol type="bool" name="config_dreamsOnlyEnabledForSystemUser" />
<java-symbol type="array" name="config_supportedDreamComplications" />
- <java-symbol type="array" name="config_dreamComplicationsEnabledByDefault" />
<java-symbol type="array" name="config_disabledDreamComponents" />
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
<java-symbol type="string" name="config_loggable_dream_prefix" />
@@ -3141,6 +3140,7 @@
<java-symbol type="string" name="language_picker_section_all" />
<java-symbol type="string" name="region_picker_section_all" />
<java-symbol type="string" name="language_picker_section_suggested" />
+ <java-symbol type="string" name="language_picker_regions_section_suggested" />
<java-symbol type="string" name="language_picker_section_suggested_bilingual" />
<java-symbol type="string" name="region_picker_section_suggested_bilingual" />
<java-symbol type="string" name="language_selection_title" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 0a5a4d5a9adb..993ecf66c25b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -221,15 +221,15 @@ public class ObjectPoolTests {
@Test
public void testRecyclePauseActivityItemItem() {
- PauseActivityItem emptyItem = PauseActivityItem.obtain(false, false, 0, false);
- PauseActivityItem item = PauseActivityItem.obtain(true, true, 5, true);
+ PauseActivityItem emptyItem = PauseActivityItem.obtain(false, false, 0, false, false);
+ PauseActivityItem item = PauseActivityItem.obtain(true, true, 5, true, true);
assertNotSame(item, emptyItem);
assertFalse(item.equals(emptyItem));
item.recycle();
assertEquals(item, emptyItem);
- PauseActivityItem item2 = PauseActivityItem.obtain(true, false, 5, true);
+ PauseActivityItem item2 = PauseActivityItem.obtain(true, false, 5, true, true);
assertSame(item, item2);
assertFalse(item2.equals(emptyItem));
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index e9bbdbee576c..b292d7dfafe2 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -234,7 +234,8 @@ public class TransactionParcelTests {
public void testPause() {
// Write to parcel
PauseActivityItem item = PauseActivityItem.obtain(true /* finished */,
- true /* userLeaving */, 135 /* configChanges */, true /* dontReport */);
+ true /* userLeaving */, 135 /* configChanges */, true /* dontReport */,
+ true /* autoEnteringPip */);
writeAndPrepareForReading(item);
// Read from parcel and assert
diff --git a/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
index c6f592447c22..2d3ed9510534 100644
--- a/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
+++ b/core/tests/coretests/src/android/widget/FloatingToolbarUtils.java
@@ -16,13 +16,12 @@
package android.widget;
-import static com.android.internal.widget.floatingtoolbar.FloatingToolbar.FLOATING_TOOLBAR_TAG;
-
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.res.Resources;
import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
@@ -33,25 +32,27 @@ import com.android.internal.R;
final class FloatingToolbarUtils {
private final UiDevice mDevice;
+ private static final BySelector TOOLBAR_CONTAINER_SELECTOR =
+ By.res("android", "floating_popup_container");
FloatingToolbarUtils() {
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
void waitForFloatingToolbarPopup() {
- mDevice.wait(Until.findObject(By.desc(FLOATING_TOOLBAR_TAG)), 500);
+ mDevice.wait(Until.findObject(TOOLBAR_CONTAINER_SELECTOR), 500);
}
void assertFloatingToolbarIsDisplayed() {
waitForFloatingToolbarPopup();
- assertThat(mDevice.hasObject(By.desc(FLOATING_TOOLBAR_TAG))).isTrue();
+ assertThat(mDevice.hasObject(TOOLBAR_CONTAINER_SELECTOR)).isTrue();
}
void assertFloatingToolbarContainsItem(String itemLabel) {
waitForFloatingToolbarPopup();
assertWithMessage("Expected to find item labelled [" + itemLabel + "]")
.that(mDevice.hasObject(
- By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
+ TOOLBAR_CONTAINER_SELECTOR.hasDescendant(By.text(itemLabel))))
.isTrue();
}
@@ -59,14 +60,14 @@ final class FloatingToolbarUtils {
waitForFloatingToolbarPopup();
assertWithMessage("Expected to not find item labelled [" + itemLabel + "]")
.that(mDevice.hasObject(
- By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(itemLabel))))
+ TOOLBAR_CONTAINER_SELECTOR.hasDescendant(By.text(itemLabel))))
.isFalse();
}
void assertFloatingToolbarContainsItemAtIndex(String itemLabel, int index) {
waitForFloatingToolbarPopup();
assertWithMessage("Expected to find item labelled [" + itemLabel + "] at index " + index)
- .that(mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+ .that(mDevice.findObject(TOOLBAR_CONTAINER_SELECTOR)
.findObjects(By.clickable(true))
.get(index)
.getChildren()
@@ -77,7 +78,7 @@ final class FloatingToolbarUtils {
void clickFloatingToolbarItem(String label) {
waitForFloatingToolbarPopup();
- mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+ mDevice.findObject(TOOLBAR_CONTAINER_SELECTOR)
.findObject(By.text(label))
.click();
}
@@ -85,13 +86,13 @@ final class FloatingToolbarUtils {
void clickFloatingToolbarOverflowItem(String label) {
// TODO: There might be a benefit to combining this with "clickFloatingToolbarItem" method.
waitForFloatingToolbarPopup();
- mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+ mDevice.findObject(TOOLBAR_CONTAINER_SELECTOR)
.findObject(By.desc(str(R.string.floating_toolbar_open_overflow_description)))
.click();
mDevice.wait(
- Until.findObject(By.desc(FLOATING_TOOLBAR_TAG).hasDescendant(By.text(label))),
+ Until.findObject(TOOLBAR_CONTAINER_SELECTOR.hasDescendant(By.text(label))),
1000);
- mDevice.findObject(By.desc(FLOATING_TOOLBAR_TAG))
+ mDevice.findObject(TOOLBAR_CONTAINER_SELECTOR)
.findObject(By.text(label))
.click();
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index f4a6f025074e..613eddd30c8a 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -298,8 +298,8 @@ public class ActivityThreadClientTest {
private void pauseActivity(ActivityClientRecord r) {
mThread.handlePauseActivity(r, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */, null /* pendingActions */,
- "test");
+ false /* userLeaving */, 0 /* configChanges */, false /* autoEnteringPip */,
+ null /* pendingActions */, "test");
}
private void stopActivity(ActivityClientRecord r) {
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 03d22ac44f6a..748032b9db62 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2065,6 +2065,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-108248992": {
+ "message": "Defer transition ready for TaskFragmentTransaction=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-106400104": {
"message": "Preload recents with %s",
"level": "DEBUG",
@@ -2113,6 +2119,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "-79016993": {
+ "message": "Continue transition ready for TaskFragmentTransaction=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-70719599": {
"message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index f8c015f28a50..8e2a59cd848a 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -39,7 +39,11 @@
<axis tag="wdth" stylevalue="100" />
<axis tag="wght" stylevalue="300" />
</font>
- <font weight="400" style="normal">RobotoStatic-Regular.ttf</font>
+ <font weight="400" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
<font weight="500" style="normal">Roboto-Regular.ttf
<axis tag="ital" stylevalue="0" />
<axis tag="wdth" stylevalue="100" />
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 8533a5994d33..b0dab90b6add 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -16,92 +16,98 @@
-->
<!-- Layout for TvPipMenuView -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tv_pip_menu"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center|top">
+ android:id="@+id/tv_pip_menu"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center|top">
<!-- Matches the PiP app content -->
- <View
+ <FrameLayout
android:id="@+id/tv_pip"
android:layout_width="0dp"
android:layout_height="0dp"
- android:alpha="0"
- android:background="@color/tv_pip_menu_background"
android:layout_marginTop="@dimen/pip_menu_outer_space"
android:layout_marginStart="@dimen/pip_menu_outer_space"
- android:layout_marginEnd="@dimen/pip_menu_outer_space"/>
-
- <ScrollView
- android:id="@+id/tv_pip_menu_scroll"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignTop="@+id/tv_pip"
- android:layout_alignStart="@+id/tv_pip"
- android:layout_alignEnd="@+id/tv_pip"
- android:layout_alignBottom="@+id/tv_pip"
- android:scrollbars="none"
- android:visibility="gone"/>
-
- <HorizontalScrollView
- android:id="@+id/tv_pip_menu_horizontal_scroll"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignTop="@+id/tv_pip"
- android:layout_alignStart="@+id/tv_pip"
- android:layout_alignEnd="@+id/tv_pip"
- android:layout_alignBottom="@+id/tv_pip"
- android:scrollbars="none">
-
- <LinearLayout
- android:id="@+id/tv_pip_menu_action_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:alpha="0">
+ android:layout_marginEnd="@dimen/pip_menu_outer_space">
- <Space
- android:layout_width="@dimen/pip_menu_button_wrapper_margin"
- android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
-
- <com.android.wm.shell.common.TvWindowMenuActionButton
- android:id="@+id/tv_pip_menu_fullscreen_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_fullscreen_white"
- android:text="@string/pip_fullscreen" />
+ <View
+ android:id="@+id/tv_pip_menu_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tv_pip_menu_background"
+ android:alpha="0"/>
- <com.android.wm.shell.common.TvWindowMenuActionButton
- android:id="@+id/tv_pip_menu_close_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_close_white"
- android:text="@string/pip_close" />
+ <View
+ android:id="@+id/tv_pip_menu_dim_layer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tv_pip_menu_dim_layer"
+ android:alpha="0"/>
- <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+ <ScrollView
+ android:id="@+id/tv_pip_menu_scroll"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scrollbars="none"
+ android:visibility="gone"/>
- <com.android.wm.shell.common.TvWindowMenuActionButton
- android:id="@+id/tv_pip_menu_move_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_move_white"
- android:text="@string/pip_move" />
+ <HorizontalScrollView
+ android:id="@+id/tv_pip_menu_horizontal_scroll"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scrollbars="none">
- <com.android.wm.shell.common.TvWindowMenuActionButton
- android:id="@+id/tv_pip_menu_expand_button"
+ <LinearLayout
+ android:id="@+id/tv_pip_menu_action_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/pip_ic_collapse"
- android:visibility="gone"
- android:text="@string/pip_collapse" />
-
- <Space
- android:layout_width="@dimen/pip_menu_button_wrapper_margin"
- android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
-
- </LinearLayout>
- </HorizontalScrollView>
+ android:orientation="horizontal"
+ android:alpha="0">
+
+ <Space
+ android:layout_width="@dimen/pip_menu_button_wrapper_margin"
+ android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
+
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_fullscreen_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_fullscreen_white"
+ android:text="@string/pip_fullscreen" />
+
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_close_white"
+ android:text="@string/pip_close" />
+
+ <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_move_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_move_white"
+ android:text="@string/pip_move" />
+
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ android:id="@+id/tv_pip_menu_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/pip_ic_collapse"
+ android:visibility="gone"
+ android:text="@string/pip_collapse" />
+
+ <Space
+ android:layout_width="@dimen/pip_menu_button_wrapper_margin"
+ android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
+
+ </LinearLayout>
+ </HorizontalScrollView>
+ </FrameLayout>
+ <!-- Frame around the content, just overlapping the corners to make them round -->
<View
android:id="@+id/tv_pip_border"
android:layout_width="0dp"
@@ -111,6 +117,7 @@
android:layout_marginEnd="@dimen/pip_menu_outer_space_frame"
android:background="@drawable/tv_pip_menu_border"/>
+ <!-- Temporarily extending the background to show an edu text hint for opening the menu -->
<FrameLayout
android:id="@+id/tv_pip_menu_edu_text_container"
android:layout_width="match_parent"
@@ -138,6 +145,7 @@
android:textAppearance="@style/TvPipEduText"/>
</FrameLayout>
+ <!-- Frame around the PiP content + edu text hint - used to highlight open menu -->
<View
android:id="@+id/tv_pip_menu_frame"
android:layout_width="match_parent"
@@ -145,7 +153,8 @@
android:layout_margin="@dimen/pip_menu_outer_space_frame"
android:background="@drawable/tv_pip_menu_border"/>
- <com.android.wm.shell.common.TvWindowMenuActionButton
+ <!-- Move menu -->
+ <com.android.wm.shell.pip.tv.TvPipMenuActionButton
android:id="@+id/tv_pip_menu_done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
index 3e71c1010278..e6933ca3fce6 100644
--- a/libs/WindowManager/Shell/res/values/colors_tv.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -25,6 +25,7 @@
<color name="tv_window_menu_icon_bg_unfocused">#990E0E0F</color>
<color name="tv_pip_menu_focus_border">#E8EAED</color>
+ <color name="tv_pip_menu_dim_layer">#990E0E0F</color>
<color name="tv_pip_menu_background">#1E232C</color>
<color name="tv_pip_edu_text">#99D2E3FC</color>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
new file mode 100644
index 000000000000..cc4db933ec9f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.activityembedding;
+
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
+
+import android.annotation.CallSuper;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+import android.window.TransitionInfo;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Wrapper to handle the ActivityEmbedding animation update in one
+ * {@link SurfaceControl.Transaction}.
+ */
+class ActivityEmbeddingAnimationAdapter {
+
+ /**
+ * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer.
+ */
+ private static final int LAYER_NO_OVERRIDE = -1;
+
+ final Animation mAnimation;
+ final TransitionInfo.Change mChange;
+ final SurfaceControl mLeash;
+
+ final Transformation mTransformation = new Transformation();
+ final float[] mMatrix = new float[9];
+ final float[] mVecs = new float[4];
+ final Rect mRect = new Rect();
+ private boolean mIsFirstFrame = true;
+ private int mOverrideLayer = LAYER_NO_OVERRIDE;
+
+ ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
+ @NonNull TransitionInfo.Change change) {
+ this(animation, change, change.getLeash());
+ }
+
+ /**
+ * @param leash the surface to animate, which is not necessary the same as
+ * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+ */
+ ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
+ @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash) {
+ mAnimation = animation;
+ mChange = change;
+ mLeash = leash;
+ }
+
+ /**
+ * Surface layer to be set at the first frame of the animation. We will not set the layer if it
+ * is set to {@link #LAYER_NO_OVERRIDE}.
+ */
+ final void overrideLayer(int layer) {
+ mOverrideLayer = layer;
+ }
+
+ /** Called on frame update. */
+ final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+ if (mIsFirstFrame) {
+ t.show(mLeash);
+ if (mOverrideLayer != LAYER_NO_OVERRIDE) {
+ t.setLayer(mLeash, mOverrideLayer);
+ }
+ mIsFirstFrame = false;
+ }
+
+ // Extract the transformation to the current time.
+ mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
+ mTransformation);
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ onAnimationUpdateInner(t);
+ }
+
+ /** To be overridden by subclasses to adjust the animation surface change. */
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ final Point offset = mChange.getEndRelOffset();
+ mTransformation.getMatrix().postTranslate(offset.x, offset.y);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ // Get current animation position.
+ final int positionX = Math.round(mMatrix[MTRANS_X]);
+ final int positionY = Math.round(mMatrix[MTRANS_Y]);
+ // The exiting surface starts at position: Change#getEndRelOffset() and moves with
+ // positionX varying. Offset our crop region by the amount we have slided so crop
+ // regions stays exactly on the original container in split.
+ final int cropOffsetX = offset.x - positionX;
+ final int cropOffsetY = offset.y - positionY;
+ final Rect cropRect = new Rect();
+ cropRect.set(mChange.getEndAbsBounds());
+ // Because window crop uses absolute position.
+ cropRect.offsetTo(0, 0);
+ cropRect.offset(cropOffsetX, cropOffsetY);
+ t.setCrop(mLeash, cropRect);
+ }
+
+ /** Called after animation finished. */
+ @CallSuper
+ void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+ onAnimationUpdate(t, mAnimation.getDuration());
+ }
+
+ final long getDurationHint() {
+ return mAnimation.computeDurationHint();
+ }
+
+ /**
+ * Should be used when the {@link TransitionInfo.Change} is in split with others, and wants to
+ * animate together as one. This adapter will offset the animation leash to make the animate of
+ * two windows look like a single window.
+ */
+ static class SplitAdapter extends ActivityEmbeddingAnimationAdapter {
+ private final boolean mIsLeftHalf;
+ private final int mWholeAnimationWidth;
+
+ /**
+ * @param isLeftHalf whether this is the left half of the animation.
+ * @param wholeAnimationWidth the whole animation windows width.
+ */
+ SplitAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
+ boolean isLeftHalf, int wholeAnimationWidth) {
+ super(animation, change);
+ mIsLeftHalf = isLeftHalf;
+ mWholeAnimationWidth = wholeAnimationWidth;
+ if (wholeAnimationWidth == 0) {
+ throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
+ }
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ final Point offset = mChange.getEndRelOffset();
+ float posX = offset.x;
+ final float posY = offset.y;
+ // This window is half of the whole animation window. Offset left/right to make it
+ // look as one with the other half.
+ mTransformation.getMatrix().getValues(mMatrix);
+ final int changeWidth = mChange.getEndAbsBounds().width();
+ final float scaleX = mMatrix[MSCALE_X];
+ final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
+ final float curOffset = changeWidth * (1 - scaleX) / 2;
+ final float offsetDiff = totalOffset - curOffset;
+ if (mIsLeftHalf) {
+ posX += offsetDiff;
+ } else {
+ posX -= offsetDiff;
+ }
+ mTransformation.getMatrix().postTranslate(posX, posY);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ }
+ }
+
+ /**
+ * Should be used for the animation of the snapshot of a {@link TransitionInfo.Change} that has
+ * size change.
+ */
+ static class SnapshotAdapter extends ActivityEmbeddingAnimationAdapter {
+
+ SnapshotAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
+ @NonNull SurfaceControl snapshotLeash) {
+ super(animation, change, snapshotLeash);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Snapshot should always be placed at the top left of the animation leash.
+ mTransformation.getMatrix().postTranslate(0, 0);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ }
+
+ @Override
+ void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+ super.onAnimationEnd(t);
+ // Remove the screenshot leash after animation is finished.
+ t.remove(mLeash);
+ }
+ }
+
+ /**
+ * Should be used for the animation of the {@link TransitionInfo.Change} that has size change.
+ */
+ static class BoundsChangeAdapter extends ActivityEmbeddingAnimationAdapter {
+
+ BoundsChangeAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change) {
+ super(animation, change);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ final Point offset = mChange.getEndRelOffset();
+ mTransformation.getMatrix().postTranslate(offset.x, offset.y);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+
+ // The following applies an inverse scale to the clip-rect so that it crops "after" the
+ // scale instead of before.
+ mVecs[1] = mVecs[2] = 0;
+ mVecs[0] = mVecs[3] = 1;
+ mTransformation.getMatrix().mapVectors(mVecs);
+ mVecs[0] = 1.f / mVecs[0];
+ mVecs[3] = 1.f / mVecs[3];
+ final Rect clipRect = mTransformation.getClipRect();
+ mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+ mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+ mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+ mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+ t.setCrop(mLeash, mRect);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
new file mode 100644
index 000000000000..7e0795d11153
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.activityembedding;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.common.ScreenshotUtils;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+/** To run the ActivityEmbedding animations. */
+class ActivityEmbeddingAnimationRunner {
+
+ private static final String TAG = "ActivityEmbeddingAnimR";
+
+ private final ActivityEmbeddingController mController;
+ @VisibleForTesting
+ final ActivityEmbeddingAnimationSpec mAnimationSpec;
+
+ ActivityEmbeddingAnimationRunner(@NonNull Context context,
+ @NonNull ActivityEmbeddingController controller) {
+ mController = controller;
+ mAnimationSpec = new ActivityEmbeddingAnimationSpec(context);
+ }
+
+ /** Creates and starts animation for ActivityEmbedding transition. */
+ void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ final Animator animator = createAnimator(info, startTransaction, finishTransaction,
+ () -> mController.onAnimationFinished(transition));
+ startTransaction.apply();
+ animator.start();
+ }
+
+ /**
+ * Sets transition animation scale settings value.
+ * @param scale The setting value of transition animation scale.
+ */
+ void setAnimScaleSetting(float scale) {
+ mAnimationSpec.setAnimScaleSetting(scale);
+ }
+
+ /** Creates the animator for the given {@link TransitionInfo}. */
+ @VisibleForTesting
+ @NonNull
+ Animator createAnimator(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Runnable animationFinishCallback) {
+ final List<ActivityEmbeddingAnimationAdapter> adapters =
+ createAnimationAdapters(info, startTransaction);
+ long duration = 0;
+ for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
+ duration = Math.max(duration, adapter.getDurationHint());
+ }
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.setDuration(duration);
+ animator.addUpdateListener((anim) -> {
+ // Update all adapters in the same transaction.
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
+ adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
+ }
+ t.apply();
+ });
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
+ adapter.onAnimationEnd(t);
+ }
+ t.apply();
+ animationFinishCallback.run();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+ return animator;
+ }
+
+ /**
+ * Creates list of {@link ActivityEmbeddingAnimationAdapter} to handle animations on all window
+ * changes.
+ */
+ @NonNull
+ private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters(
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getMode() == TRANSIT_CHANGE
+ && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
+ return createChangeAnimationAdapters(info, startTransaction);
+ }
+ }
+ if (Transitions.isClosingType(info.getType())) {
+ return createCloseAnimationAdapters(info);
+ }
+ return createOpenAnimationAdapters(info);
+ }
+
+ @NonNull
+ private List<ActivityEmbeddingAnimationAdapter> createOpenAnimationAdapters(
+ @NonNull TransitionInfo info) {
+ return createOpenCloseAnimationAdapters(info, true /* isOpening */,
+ mAnimationSpec::loadOpenAnimation);
+ }
+
+ @NonNull
+ private List<ActivityEmbeddingAnimationAdapter> createCloseAnimationAdapters(
+ @NonNull TransitionInfo info) {
+ return createOpenCloseAnimationAdapters(info, false /* isOpening */,
+ mAnimationSpec::loadCloseAnimation);
+ }
+
+ /**
+ * Creates {@link ActivityEmbeddingAnimationAdapter} for OPEN and CLOSE types of transition.
+ * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type.
+ */
+ @NonNull
+ private List<ActivityEmbeddingAnimationAdapter> createOpenCloseAnimationAdapters(
+ @NonNull TransitionInfo info, boolean isOpening,
+ @NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider) {
+ // We need to know if the change window is only a partial of the whole animation screen.
+ // If so, we will need to adjust it to make the whole animation screen looks like one.
+ final List<TransitionInfo.Change> openingChanges = new ArrayList<>();
+ final List<TransitionInfo.Change> closingChanges = new ArrayList<>();
+ final Rect openingWholeScreenBounds = new Rect();
+ final Rect closingWholeScreenBounds = new Rect();
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final Rect bounds = new Rect(change.getEndAbsBounds());
+ final Point offset = change.getEndRelOffset();
+ bounds.offsetTo(offset.x, offset.y);
+ if (Transitions.isOpeningType(change.getMode())) {
+ openingChanges.add(change);
+ openingWholeScreenBounds.union(bounds);
+ } else {
+ closingChanges.add(change);
+ closingWholeScreenBounds.union(bounds);
+ }
+ }
+
+ // For OPEN transition, open windows should be above close windows.
+ // For CLOSE transition, open windows should be below close windows.
+ int offsetLayer = TYPE_LAYER_OFFSET;
+ final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
+ for (TransitionInfo.Change change : openingChanges) {
+ final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
+ change, animationProvider, openingWholeScreenBounds);
+ if (isOpening) {
+ adapter.overrideLayer(offsetLayer++);
+ }
+ adapters.add(adapter);
+ }
+ for (TransitionInfo.Change change : closingChanges) {
+ final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
+ change, animationProvider, closingWholeScreenBounds);
+ if (!isOpening) {
+ adapter.overrideLayer(offsetLayer++);
+ }
+ adapters.add(adapter);
+ }
+ return adapters;
+ }
+
+ @NonNull
+ private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter(
+ @NonNull TransitionInfo.Change change,
+ @NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider,
+ @NonNull Rect wholeAnimationBounds) {
+ final Animation animation = animationProvider.apply(change, wholeAnimationBounds);
+ final Rect bounds = new Rect(change.getEndAbsBounds());
+ final Point offset = change.getEndRelOffset();
+ bounds.offsetTo(offset.x, offset.y);
+ if (bounds.left == wholeAnimationBounds.left
+ && bounds.right != wholeAnimationBounds.right) {
+ // This is the left split of the whole animation window.
+ return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
+ true /* isLeftHalf */, wholeAnimationBounds.width());
+ } else if (bounds.left != wholeAnimationBounds.left
+ && bounds.right == wholeAnimationBounds.right) {
+ // This is the right split of the whole animation window.
+ return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
+ false /* isLeftHalf */, wholeAnimationBounds.width());
+ }
+ // Open/close window that fills the whole animation.
+ return new ActivityEmbeddingAnimationAdapter(animation, change);
+ }
+
+ @NonNull
+ private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
+ final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getMode() == TRANSIT_CHANGE
+ && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
+ // This is the window with bounds change.
+ final WindowContainerToken parentToken = change.getParent();
+ final Rect parentBounds;
+ if (parentToken != null) {
+ TransitionInfo.Change parentChange = info.getChange(parentToken);
+ parentBounds = parentChange != null
+ ? parentChange.getEndAbsBounds()
+ : change.getEndAbsBounds();
+ } else {
+ parentBounds = change.getEndAbsBounds();
+ }
+ final Animation[] animations =
+ mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
+ // Adapter for the starting screenshot leash.
+ final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction);
+ if (screenshotLeash != null) {
+ // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
+ adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
+ animations[0], change, screenshotLeash));
+ } else {
+ Log.e(TAG, "Failed to take screenshot for change=" + change);
+ }
+ // Adapter for the ending bounds changed leash.
+ adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
+ animations[1], change));
+ continue;
+ }
+
+ // These are the other windows that don't have bounds change in the same transition.
+ final Animation animation;
+ if (!TransitionInfo.isIndependent(change, info)) {
+ // No-op if it will be covered by the changing parent window.
+ animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
+ } else if (Transitions.isClosingType(change.getMode())) {
+ animation = mAnimationSpec.createChangeBoundsCloseAnimation(change);
+ } else {
+ animation = mAnimationSpec.createChangeBoundsOpenAnimation(change);
+ }
+ adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change));
+ }
+ return adapters;
+ }
+
+ /** Takes a screenshot of the given {@link TransitionInfo.Change} surface. */
+ @Nullable
+ private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change,
+ @NonNull SurfaceControl.Transaction startTransaction) {
+ final Rect cropBounds = new Rect(change.getStartAbsBounds());
+ cropBounds.offsetTo(0, 0);
+ return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds,
+ Integer.MAX_VALUE);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
new file mode 100644
index 000000000000..6f06f28caff2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.activityembedding;
+
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+import android.window.TransitionInfo;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.policy.TransitionAnimation;
+import com.android.wm.shell.transition.Transitions;
+
+/** Animation spec for ActivityEmbedding transition. */
+// TODO(b/206557124): provide an easier way to customize animation
+class ActivityEmbeddingAnimationSpec {
+
+ private static final String TAG = "ActivityEmbeddingAnimSpec";
+ private static final int CHANGE_ANIMATION_DURATION = 517;
+ private static final int CHANGE_ANIMATION_FADE_DURATION = 80;
+ private static final int CHANGE_ANIMATION_FADE_OFFSET = 30;
+
+ private final Context mContext;
+ private final TransitionAnimation mTransitionAnimation;
+ private final Interpolator mFastOutExtraSlowInInterpolator;
+ private final LinearInterpolator mLinearInterpolator;
+ private float mTransitionAnimationScaleSetting;
+
+ ActivityEmbeddingAnimationSpec(@NonNull Context context) {
+ mContext = context;
+ mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG);
+ mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator(
+ mContext, android.R.interpolator.fast_out_extra_slow_in);
+ mLinearInterpolator = new LinearInterpolator();
+ }
+
+ /**
+ * Sets transition animation scale settings value.
+ * @param scale The setting value of transition animation scale.
+ */
+ void setAnimScaleSetting(float scale) {
+ mTransitionAnimationScaleSetting = scale;
+ }
+
+ /** For window that doesn't need to be animated. */
+ @NonNull
+ static Animation createNoopAnimation(@NonNull TransitionInfo.Change change) {
+ // Noop but just keep the window showing/hiding.
+ final float alpha = Transitions.isClosingType(change.getMode()) ? 0f : 1f;
+ return new AlphaAnimation(alpha, alpha);
+ }
+
+ /** Animation for window that is opening in a change transition. */
+ @NonNull
+ Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change) {
+ final Rect bounds = change.getEndAbsBounds();
+ final Point offset = change.getEndRelOffset();
+ // The window will be animated in from left or right depends on its position.
+ final int startLeft = offset.x == 0 ? -bounds.width() : bounds.width();
+
+ // The position should be 0-based as we will post translate in
+ // ActivityEmbeddingAnimationAdapter#onAnimationUpdate
+ final Animation animation = new TranslateAnimation(startLeft, 0, 0, 0);
+ animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+ animation.setDuration(CHANGE_ANIMATION_DURATION);
+ animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ /** Animation for window that is closing in a change transition. */
+ @NonNull
+ Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change) {
+ final Rect bounds = change.getEndAbsBounds();
+ final Point offset = change.getEndRelOffset();
+ // The window will be animated out to left or right depends on its position.
+ final int endLeft = offset.x == 0 ? -bounds.width() : bounds.width();
+
+ // The position should be 0-based as we will post translate in
+ // ActivityEmbeddingAnimationAdapter#onAnimationUpdate
+ final Animation animation = new TranslateAnimation(0, endLeft, 0, 0);
+ animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+ animation.setDuration(CHANGE_ANIMATION_DURATION);
+ animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ /**
+ * Animation for window that is changing (bounds change) in a change transition.
+ * @return the return array always has two elements. The first one is for the start leash, and
+ * the second one is for the end leash.
+ */
+ @NonNull
+ Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
+ // Both start bounds and end bounds are in screen coordinates. We will post translate
+ // to the local coordinates in ActivityEmbeddingAnimationAdapter#onAnimationUpdate
+ final Rect startBounds = change.getStartAbsBounds();
+ final Rect endBounds = change.getEndAbsBounds();
+ float scaleX = ((float) startBounds.width()) / endBounds.width();
+ float scaleY = ((float) startBounds.height()) / endBounds.height();
+ // Start leash is a child of the end leash. Reverse the scale so that the start leash won't
+ // be scaled up with its parent.
+ float startScaleX = 1.f / scaleX;
+ float startScaleY = 1.f / scaleY;
+
+ // The start leash will be fade out.
+ final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */);
+ final Animation startAlpha = new AlphaAnimation(1f, 0f);
+ startAlpha.setInterpolator(mLinearInterpolator);
+ startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION);
+ startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET);
+ startSet.addAnimation(startAlpha);
+ final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY,
+ startScaleY);
+ startScale.setInterpolator(mFastOutExtraSlowInInterpolator);
+ startScale.setDuration(CHANGE_ANIMATION_DURATION);
+ startSet.addAnimation(startScale);
+ startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(),
+ endBounds.height());
+ startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+ // The end leash will be moved into the end position while scaling.
+ final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */);
+ endSet.setInterpolator(mFastOutExtraSlowInInterpolator);
+ final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1);
+ endScale.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(endScale);
+ // The position should be 0-based as we will post translate in
+ // ActivityEmbeddingAnimationAdapter#onAnimationUpdate
+ final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0,
+ 0, 0);
+ endTranslate.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(endTranslate);
+ // The end leash is resizing, we should update the window crop based on the clip rect.
+ final Rect startClip = new Rect(startBounds);
+ final Rect endClip = new Rect(endBounds);
+ startClip.offsetTo(0, 0);
+ endClip.offsetTo(0, 0);
+ final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+ clipAnim.setDuration(CHANGE_ANIMATION_DURATION);
+ endSet.addAnimation(clipAnim);
+ endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(),
+ parentBounds.height());
+ endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+ return new Animation[]{startSet, endSet};
+ }
+
+ @NonNull
+ Animation loadOpenAnimation(@NonNull TransitionInfo.Change change,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = Transitions.isOpeningType(change.getMode());
+ final Animation animation;
+ // TODO(b/207070762):
+ // 1. Implement clearTop version: R.anim.task_fragment_clear_top_close_enter/exit
+ // 2. Implement edgeExtension version
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? R.anim.task_fragment_open_enter
+ : R.anim.task_fragment_open_exit);
+ final Rect bounds = change.getEndAbsBounds();
+ animation.initialize(bounds.width(), bounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+
+ @NonNull
+ Animation loadCloseAnimation(@NonNull TransitionInfo.Change change,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = Transitions.isOpeningType(change.getMode());
+ final Animation animation;
+ // TODO(b/207070762):
+ // 1. Implement clearTop version: R.anim.task_fragment_clear_top_close_enter/exit
+ // 2. Implement edgeExtension version
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? R.anim.task_fragment_close_enter
+ : R.anim.task_fragment_close_exit);
+ final Rect bounds = change.getEndAbsBounds();
+ animation.initialize(bounds.width(), bounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index b305897b77ae..e0004fcaa060 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -18,8 +18,11 @@ package com.android.wm.shell.activityembedding;
import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+import static java.util.Objects.requireNonNull;
+
import android.content.Context;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -28,6 +31,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -37,15 +41,37 @@ import com.android.wm.shell.transition.Transitions;
public class ActivityEmbeddingController implements Transitions.TransitionHandler {
private final Context mContext;
- private final Transitions mTransitions;
-
- public ActivityEmbeddingController(Context context, ShellInit shellInit,
- Transitions transitions) {
- mContext = context;
- mTransitions = transitions;
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- shellInit.addInitCallback(this::onInit, this);
- }
+ @VisibleForTesting
+ final Transitions mTransitions;
+ @VisibleForTesting
+ final ActivityEmbeddingAnimationRunner mAnimationRunner;
+
+ /**
+ * Keeps track of the currently-running transition callback associated with each transition
+ * token.
+ */
+ private final ArrayMap<IBinder, Transitions.TransitionFinishCallback> mTransitionCallbacks =
+ new ArrayMap<>();
+
+ private ActivityEmbeddingController(@NonNull Context context, @NonNull ShellInit shellInit,
+ @NonNull Transitions transitions) {
+ mContext = requireNonNull(context);
+ mTransitions = requireNonNull(transitions);
+ mAnimationRunner = new ActivityEmbeddingAnimationRunner(context, this);
+
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ /**
+ * Creates {@link ActivityEmbeddingController}, returns {@code null} if the feature is not
+ * supported.
+ */
+ @Nullable
+ public static ActivityEmbeddingController create(@NonNull Context context,
+ @NonNull ShellInit shellInit, @NonNull Transitions transitions) {
+ return Transitions.ENABLE_SHELL_TRANSITIONS
+ ? new ActivityEmbeddingController(context, shellInit, transitions)
+ : null;
}
/** Registers to handle transitions. */
@@ -66,9 +92,9 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
}
}
- // TODO(b/207070762) Implement AE animation.
- startTransaction.apply();
- finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ // Start ActivityEmbedding animation.
+ mTransitionCallbacks.put(transition, finishCallback);
+ mAnimationRunner.startAnimation(transition, info, startTransaction, finishTransaction);
return true;
}
@@ -79,6 +105,21 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
return null;
}
+ @Override
+ public void setAnimScaleSetting(float scale) {
+ mAnimationRunner.setAnimScaleSetting(scale);
+ }
+
+ /** Called when the animation is finished. */
+ void onAnimationFinished(@NonNull IBinder transition) {
+ final Transitions.TransitionFinishCallback callback =
+ mTransitionCallbacks.remove(transition);
+ if (callback == null) {
+ throw new IllegalStateException("No finish callback found");
+ }
+ callback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ }
+
private static boolean isEmbedded(@NonNull TransitionInfo.Change change) {
return (change.getFlags() & FLAG_IS_EMBEDDED) != 0;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 2c02006c8ca5..99b8885acdef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -821,7 +821,7 @@ public class Bubble implements BubbleViewProvider {
/**
* Description of current bubble state.
*/
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ public void dump(@NonNull PrintWriter pw) {
pw.print("key: "); pw.println(mKey);
pw.print(" showInShade: "); pw.println(showInShade());
pw.print(" showDot: "); pw.println(showDot());
@@ -831,7 +831,7 @@ public class Bubble implements BubbleViewProvider {
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
if (mExpandedView != null) {
- mExpandedView.dump(pw, args);
+ mExpandedView.dump(pw);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index de26b54971ca..dcbb272feab8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -72,7 +72,6 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
@@ -100,6 +99,7 @@ import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -159,6 +159,7 @@ public class BubbleController implements ConfigurationChangeListener {
private final TaskViewTransitions mTaskViewTransitions;
private final SyncTransactionQueue mSyncQueue;
private final ShellController mShellController;
+ private final ShellCommandHandler mShellCommandHandler;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -229,6 +230,7 @@ public class BubbleController implements ConfigurationChangeListener {
public BubbleController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
@@ -252,6 +254,7 @@ public class BubbleController implements ConfigurationChangeListener {
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mLauncherApps = launcherApps;
mBarService = statusBarService == null
@@ -431,6 +434,7 @@ public class BubbleController implements ConfigurationChangeListener {
mCurrentProfiles = userProfiles;
mShellController.addConfigurationChangeListener(this);
+ mShellCommandHandler.addDumpCallback(this::dump, this);
}
@VisibleForTesting
@@ -538,7 +542,6 @@ public class BubbleController implements ConfigurationChangeListener {
if (mNotifEntryToExpandOnShadeUnlock != null) {
expandStackAndSelectBubble(mNotifEntryToExpandOnShadeUnlock);
- mNotifEntryToExpandOnShadeUnlock = null;
}
updateStack();
@@ -925,15 +928,6 @@ public class BubbleController implements ConfigurationChangeListener {
return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
- private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback) {
- if (mBubbleData.isSummarySuppressed(groupKey)) {
- mBubbleData.removeSuppressedSummary(groupKey);
- if (callback != null) {
- callback.accept(mBubbleData.getSummaryKey(groupKey));
- }
- }
- }
-
/** Promote the provided bubble from the overflow view. */
public void promoteBubbleFromOverflow(Bubble bubble) {
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
@@ -1519,14 +1513,15 @@ public class BubbleController implements ConfigurationChangeListener {
/**
* Description of current bubble state.
*/
- private void dump(PrintWriter pw, String[] args) {
+ private void dump(PrintWriter pw, String prefix) {
pw.println("BubbleController state:");
- mBubbleData.dump(pw, args);
+ mBubbleData.dump(pw);
pw.println();
if (mStackView != null) {
- mStackView.dump(pw, args);
+ mStackView.dump(pw);
}
pw.println();
+ mImpl.mCachedState.dump(pw);
}
/**
@@ -1711,28 +1706,12 @@ public class BubbleController implements ConfigurationChangeListener {
}
@Override
- public boolean isStackExpanded() {
- return mCachedState.isStackExpanded();
- }
-
- @Override
@Nullable
public Bubble getBubbleWithShortcutId(String shortcutId) {
return mCachedState.getBubbleWithShortcutId(shortcutId);
}
@Override
- public void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
- Executor callbackExecutor) {
- mMainExecutor.execute(() -> {
- Consumer<String> cb = callback != null
- ? (key) -> callbackExecutor.execute(() -> callback.accept(key))
- : null;
- BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, cb);
- });
- }
-
- @Override
public void collapseStack() {
mMainExecutor.execute(() -> {
BubbleController.this.collapseStack();
@@ -1761,13 +1740,6 @@ public class BubbleController implements ConfigurationChangeListener {
}
@Override
- public void openBubbleOverflow() {
- mMainExecutor.execute(() -> {
- BubbleController.this.openBubbleOverflow();
- });
- }
-
- @Override
public boolean handleDismissalInterception(BubbleEntry entry,
@Nullable List<BubbleEntry> children, IntConsumer removeCallback,
Executor callbackExecutor) {
@@ -1882,18 +1854,6 @@ public class BubbleController implements ConfigurationChangeListener {
mMainExecutor.execute(
() -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
}
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- try {
- mMainExecutor.executeBlocking(() -> {
- BubbleController.this.dump(pw, args);
- mCachedState.dump(pw);
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to dump BubbleController in 2s");
- }
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index fa86c8436647..c64133f0b668 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -1136,7 +1136,7 @@ public class BubbleData {
/**
* Description of current bubble data state.
*/
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(PrintWriter pw) {
pw.print("selected: ");
pw.println(mSelectedBubble != null
? mSelectedBubble.getKey()
@@ -1147,13 +1147,13 @@ public class BubbleData {
pw.print("stack bubble count: ");
pw.println(mBubbles.size());
for (Bubble bubble : mBubbles) {
- bubble.dump(pw, args);
+ bubble.dump(pw);
}
pw.print("overflow bubble count: ");
pw.println(mOverflowBubbles.size());
for (Bubble bubble : mOverflowBubbles) {
- bubble.dump(pw, args);
+ bubble.dump(pw);
}
pw.print("summaryKeys: ");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 2666a0e186b3..cfbe1b3caf7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -1044,7 +1044,7 @@ public class BubbleExpandedView extends LinearLayout {
/**
* Description of current expanded view state.
*/
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ public void dump(@NonNull PrintWriter pw) {
pw.print("BubbleExpandedView");
pw.print(" taskId: "); pw.println(mTaskId);
pw.print(" stackView: "); pw.println(mStackView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2d0be066beb5..5bf88b119661 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -299,7 +299,7 @@ public class BubbleStackView extends FrameLayout
private BubblesNavBarGestureTracker mBubblesNavBarGestureTracker;
/** Description of current animation controller state. */
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(PrintWriter pw) {
pw.println("Stack view state:");
String bubblesOnScreen = BubbleDebugConfig.formatBubblesString(
@@ -313,8 +313,8 @@ public class BubbleStackView extends FrameLayout
pw.print(" expandedContainerMatrix: ");
pw.println(mExpandedViewContainer.getAnimationMatrix());
- mStackAnimationController.dump(pw, args);
- mExpandedAnimationController.dump(pw, args);
+ mStackAnimationController.dump(pw);
+ mExpandedAnimationController.dump(pw);
if (mExpandedBubble != null) {
pw.println("Expanded bubble state:");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 37b96ffe5cd1..0e97e9e74114 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -35,7 +35,6 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.common.annotations.ExternalThread;
-import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.HashMap;
@@ -91,18 +90,6 @@ public interface Bubbles {
*/
boolean isBubbleExpanded(String key);
- /** @return {@code true} if stack of bubbles is expanded or not. */
- boolean isStackExpanded();
-
- /**
- * Removes a group key indicating that the summary for this group should no longer be
- * suppressed.
- *
- * @param callback If removed, this callback will be called with the summary key of the group
- */
- void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
- Executor callbackExecutor);
-
/** Tell the stack of bubbles to collapse. */
void collapseStack();
@@ -130,9 +117,6 @@ public interface Bubbles {
/** Called for any taskbar changes. */
void onTaskbarChanged(Bundle b);
- /** Open the overflow view. */
- void openBubbleOverflow();
-
/**
* We intercept notification entries (including group summaries) dismissed by the user when
* there is an active bubble associated with it. We do this so that developers can still
@@ -252,9 +236,6 @@ public interface Bubbles {
*/
void onUserRemoved(int removedUserId);
- /** Description of current bubble state. */
- void dump(PrintWriter pw, String[] args);
-
/** Listener to find out about stack expansion / collapse events. */
interface BubbleExpandListener {
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index b521cb6a3d38..ae434bcec6c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -468,7 +468,7 @@ public class ExpandedAnimationController
}
/** Description of current animation controller state. */
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(PrintWriter pw) {
pw.println("ExpandedAnimationController state:");
pw.print(" isActive: "); pw.println(isActiveController());
pw.print(" animatingExpand: "); pw.println(mAnimatingExpand);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 0a1b4d70fb2b..4e2cbfd82fcc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -431,7 +431,7 @@ public class StackAnimationController extends
}
/** Description of current animation controller state. */
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(PrintWriter pw) {
pw.println("StackAnimationController state:");
pw.print(" isActive: "); pw.println(isActiveController());
pw.print(" restingStackPos: ");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index e22c9517f4ab..8022e9b1cd81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -66,6 +66,7 @@ public abstract class TvPipModule {
@Provides
static Optional<Pip> providePip(
Context context,
+ ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
@@ -84,6 +85,7 @@ public abstract class TvPipModule {
return Optional.of(
TvPipController.create(
context,
+ shellInit,
shellController,
tvPipBoundsState,
tvPipBoundsAlgorithm,
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 a6a04cf67b3c..ceaa64ef8290 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
@@ -627,11 +627,12 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static ActivityEmbeddingController provideActivityEmbeddingController(
+ static Optional<ActivityEmbeddingController> provideActivityEmbeddingController(
Context context,
ShellInit shellInit,
Transitions transitions) {
- return new ActivityEmbeddingController(context, shellInit, transitions);
+ return Optional.ofNullable(
+ ActivityEmbeddingController.create(context, shellInit, transitions));
}
//
@@ -686,7 +687,7 @@ public abstract class WMShellBaseModule {
Optional<RecentTasksController> recentTasksOptional,
Optional<OneHandedController> oneHandedControllerOptional,
Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
- ActivityEmbeddingController activityEmbeddingOptional,
+ Optional<ActivityEmbeddingController> activityEmbeddingOptional,
Transitions transitions,
StartingWindowController startingWindow,
@ShellCreateTriggerOverride Optional<Object> overriddenCreateTrigger) {
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 2bcc134f9dd5..4fe32556a94d 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
@@ -145,6 +145,7 @@ public abstract class WMShellModule {
@Provides
static BubbleController provideBubbleController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
BubbleData data,
FloatingContentCoordinator floatingContentCoordinator,
@@ -165,7 +166,7 @@ public abstract class WMShellModule {
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
- return new BubbleController(context, shellInit, shellController, data,
+ return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
null /* synchronizer */, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, userManager,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 76c0f41997ad..7129165a78dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -37,16 +37,6 @@ public interface OneHanded {
}
/**
- * Return one handed settings enabled or not.
- */
- boolean isOneHandedEnabled();
-
- /**
- * Return swipe to notification settings enabled or not.
- */
- boolean isSwipeToNotificationEnabled();
-
- /**
* Enters one handed mode.
*/
void startOneHanded();
@@ -80,9 +70,4 @@ public interface OneHanded {
* transition start or finish
*/
void registerTransitionCallback(OneHandedTransitionCallback callback);
-
- /**
- * Notifies when user switch complete
- */
- void onUserSwitch(int userId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 9149204b94ce..e0c4fe8c4fba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -59,6 +59,7 @@ import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.UserChangeListener;
import java.io.PrintWriter;
@@ -67,7 +68,7 @@ import java.io.PrintWriter;
*/
public class OneHandedController implements RemoteCallable<OneHandedController>,
DisplayChangeController.OnDisplayChangingListener, ConfigurationChangeListener,
- KeyguardChangeListener {
+ KeyguardChangeListener, UserChangeListener {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -76,8 +77,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
public static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
- private volatile boolean mIsOneHandedEnabled;
- private volatile boolean mIsSwipeToNotificationEnabled;
+ private boolean mIsOneHandedEnabled;
+ private boolean mIsSwipeToNotificationEnabled;
private boolean mIsShortcutEnabled;
private boolean mTaskChangeToExit;
private boolean mLockedDisabled;
@@ -294,6 +295,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mState.addSListeners(mTutorialHandler);
mShellController.addConfigurationChangeListener(this);
mShellController.addKeyguardChangeListener(this);
+ mShellController.addUserChangeListener(this);
}
public OneHanded asOneHanded() {
@@ -627,7 +629,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
stopOneHanded();
}
- private void onUserSwitch(int newUserId) {
+ @Override
+ public void onUserChanged(int newUserId, @NonNull Context userContext) {
unregisterSettingObservers();
mUserId = newUserId;
registerSettingObservers(newUserId);
@@ -718,18 +721,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
@Override
- public boolean isOneHandedEnabled() {
- // This is volatile so return directly
- return mIsOneHandedEnabled;
- }
-
- @Override
- public boolean isSwipeToNotificationEnabled() {
- // This is volatile so return directly
- return mIsSwipeToNotificationEnabled;
- }
-
- @Override
public void startOneHanded() {
mMainExecutor.execute(() -> {
OneHandedController.this.startOneHanded();
@@ -770,13 +761,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
OneHandedController.this.registerTransitionCallback(callback);
});
}
-
- @Override
- public void onUserSwitch(int userId) {
- mMainExecutor.execute(() -> {
- OneHandedController.this.onUserSwitch(userId);
- });
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 93172f82edd1..c06881ae6ad7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -51,12 +51,6 @@ public interface Pip {
}
/**
- * Registers the session listener for the current user.
- */
- default void registerSessionListenerForCurrentUser() {
- }
-
- /**
* Sets both shelf visibility and its height.
*
* @param visible visibility of shelf.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index fc97f310ad4e..ac3407dd1ca1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -92,6 +92,7 @@ import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.UserChangeListener;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -105,7 +106,8 @@ import java.util.function.Consumer;
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
public class PipController implements PipTransitionController.PipTransitionCallback,
- RemoteCallable<PipController>, ConfigurationChangeListener, KeyguardChangeListener {
+ RemoteCallable<PipController>, ConfigurationChangeListener, KeyguardChangeListener,
+ UserChangeListener {
private static final String TAG = "PipController";
private Context mContext;
@@ -528,7 +530,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
});
mOneHandedController.ifPresent(controller -> {
- controller.asOneHanded().registerTransitionCallback(
+ controller.registerTransitionCallback(
new OneHandedTransitionCallback() {
@Override
public void onStartFinished(Rect bounds) {
@@ -542,8 +544,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
});
});
+ mMediaController.registerSessionListenerForCurrentUser();
+
mShellController.addConfigurationChangeListener(this);
mShellController.addKeyguardChangeListener(this);
+ mShellController.addUserChangeListener(this);
}
@Override
@@ -557,6 +562,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
+ public void onUserChanged(int newUserId, @NonNull Context userContext) {
+ // Re-register the media session listener when switching users
+ mMediaController.registerSessionListenerForCurrentUser();
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newConfig) {
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
@@ -644,10 +655,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
}
- private void registerSessionListenerForCurrentUser() {
- mMediaController.registerSessionListenerForCurrentUser();
- }
-
private void onSystemUiStateChanged(boolean isValidState, int flag) {
mTouchHandler.onSystemUiStateChanged(isValidState);
}
@@ -968,13 +975,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void registerSessionListenerForCurrentUser() {
- mMainExecutor.execute(() -> {
- PipController.this.registerSessionListenerForCurrentUser();
- });
- }
-
- @Override
public void setShelfHeight(boolean visible, int height) {
mMainExecutor.execute(() -> {
PipController.this.setShelfHeight(visible, height);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a24d9618032d..4e1b0469eb96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -32,6 +32,8 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.view.Gravity;
+import androidx.annotation.NonNull;
+
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -51,6 +53,8 @@ import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.UserChangeListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -64,7 +68,7 @@ import java.util.Set;
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate,
TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener,
- ConfigurationChangeListener {
+ ConfigurationChangeListener, UserChangeListener {
private static final String TAG = "TvPipController";
static final boolean DEBUG = false;
@@ -105,6 +109,11 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final PipMediaController mPipMediaController;
private final TvPipNotificationController mPipNotificationController;
private final TvPipMenuController mTvPipMenuController;
+ private final PipTransitionController mPipTransitionController;
+ private final TaskStackListenerImpl mTaskStackListener;
+ private final PipParamsChangedForwarder mPipParamsChangedForwarder;
+ private final DisplayController mDisplayController;
+ private final WindowManagerShellWrapper mWmShellWrapper;
private final ShellExecutor mMainExecutor;
private final TvPipImpl mImpl = new TvPipImpl();
@@ -121,6 +130,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public static Pip create(
Context context,
+ ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
@@ -138,6 +148,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
ShellExecutor mainExecutor) {
return new TvPipController(
context,
+ shellInit,
shellController,
tvPipBoundsState,
tvPipBoundsAlgorithm,
@@ -157,6 +168,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private TvPipController(
Context context,
+ ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
@@ -170,11 +182,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
- WindowManagerShellWrapper wmShell,
+ WindowManagerShellWrapper wmShellWrapper,
ShellExecutor mainExecutor) {
mContext = context;
mMainExecutor = mainExecutor;
mShellController = shellController;
+ mDisplayController = displayController;
mTvPipBoundsState = tvPipBoundsState;
mTvPipBoundsState.setDisplayId(context.getDisplayId());
@@ -193,16 +206,32 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mAppOpsListener = pipAppOpsListener;
mPipTaskOrganizer = pipTaskOrganizer;
- pipTransitionController.registerPipTransitionCallback(this);
+ mPipTransitionController = pipTransitionController;
+ mPipParamsChangedForwarder = pipParamsChangedForwarder;
+ mTaskStackListener = taskStackListener;
+ mWmShellWrapper = wmShellWrapper;
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mPipTransitionController.registerPipTransitionCallback(this);
loadConfigurations();
- registerPipParamsChangedListener(pipParamsChangedForwarder);
- registerTaskStackListenerCallback(taskStackListener);
- registerWmShellPinnedStackListener(wmShell);
- displayController.addDisplayWindowListener(this);
+ registerPipParamsChangedListener(mPipParamsChangedForwarder);
+ registerTaskStackListenerCallback(mTaskStackListener);
+ registerWmShellPinnedStackListener(mWmShellWrapper);
+ registerSessionListenerForCurrentUser();
+ mDisplayController.addDisplayWindowListener(this);
mShellController.addConfigurationChangeListener(this);
+ mShellController.addUserChangeListener(this);
+ }
+
+ @Override
+ public void onUserChanged(int newUserId, @NonNull Context userContext) {
+ // Re-register the media session listener when switching users
+ registerSessionListenerForCurrentUser();
}
@Override
@@ -679,11 +708,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
private class TvPipImpl implements Pip {
- @Override
- public void registerSessionListenerForCurrentUser() {
- mMainExecutor.execute(() -> {
- TvPipController.this.registerSessionListenerForCurrentUser();
- });
- }
+ // Not used
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 4d7c8465bcc2..97e017a65b04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -97,6 +97,9 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final ImageView mArrowLeft;
private final TvWindowMenuActionButton mA11yDoneButton;
+ private final View mPipBackground;
+ private final View mDimLayer;
+
private final ScrollView mScrollView;
private final HorizontalScrollView mHorizontalScrollView;
private View mFocusedButton;
@@ -148,6 +151,9 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mExpandButton = findViewById(R.id.tv_pip_menu_expand_button);
mExpandButton.setOnClickListener(this);
+ mPipBackground = findViewById(R.id.tv_pip_menu_background);
+ mDimLayer = findViewById(R.id.tv_pip_menu_dim_layer);
+
mScrollView = findViewById(R.id.tv_pip_menu_scroll);
mHorizontalScrollView = findViewById(R.id.tv_pip_menu_horizontal_scroll);
@@ -231,7 +237,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mCurrentPipBounds.width() / (float) mCurrentPipBounds.height(),
finishBounds.width() / (float) finishBounds.height());
if (ratioChanged) {
- mPipView.animate()
+ mPipBackground.animate()
.alpha(1f)
.setInterpolator(TvPipInterpolators.EXIT)
.setDuration(mResizeAnimationDuration / 2)
@@ -272,7 +278,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
"%s: onPipTransitionFinished()", TAG);
// Fade in content by fading out view on top.
- mPipView.animate()
+ mPipBackground.animate()
.alpha(0f)
.setDuration(mResizeAnimationDuration / 2)
.setInterpolator(TvPipInterpolators.ENTER)
@@ -770,6 +776,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
refocusPreviousButton();
}
animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
+ animateAlphaTo(show ? 1 : 0, mDimLayer);
}
private void setFrameHighlighted(boolean highlighted) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java
index 1c0b35894acd..9df863163b50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java
@@ -21,13 +21,13 @@ package com.android.wm.shell.sysui;
*/
public interface KeyguardChangeListener {
/**
- * Notifies the Shell that the keyguard is showing (and if so, whether it is occluded).
+ * Called when the keyguard is showing (and if so, whether it is occluded).
*/
default void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
boolean animatingDismiss) {}
/**
- * Notifies the Shell when the keyguard dismiss animation has finished.
+ * Called when the keyguard dismiss animation has finished.
*
* TODO(b/206741900) deprecate this path once we're able to animate the PiP window as part of
* keyguard dismiss animation.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index 52ffb46bb39c..57993948886b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -25,7 +25,9 @@ import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SYSUI_EVENTS;
+import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
@@ -36,6 +38,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import java.io.PrintWriter;
+import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -53,6 +56,9 @@ public class ShellController {
new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<KeyguardChangeListener> mKeyguardChangeListeners =
new CopyOnWriteArrayList<>();
+ private final CopyOnWriteArrayList<UserChangeListener> mUserChangeListeners =
+ new CopyOnWriteArrayList<>();
+
private Configuration mLastConfiguration;
@@ -102,6 +108,22 @@ public class ShellController {
mKeyguardChangeListeners.remove(listener);
}
+ /**
+ * Adds a new user-change listener. The user change callbacks are not made in any
+ * particular order.
+ */
+ public void addUserChangeListener(UserChangeListener listener) {
+ mUserChangeListeners.remove(listener);
+ mUserChangeListeners.add(listener);
+ }
+
+ /**
+ * Removes an existing user-change listener.
+ */
+ public void removeUserChangeListener(UserChangeListener listener) {
+ mUserChangeListeners.remove(listener);
+ }
+
@VisibleForTesting
void onConfigurationChanged(Configuration newConfig) {
// The initial config is send on startup and doesn't trigger listener callbacks
@@ -144,6 +166,8 @@ public class ShellController {
@VisibleForTesting
void onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss) {
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "Keyguard visibility changed: visible=%b "
+ + "occluded=%b animatingDismiss=%b", visible, occluded, animatingDismiss);
for (KeyguardChangeListener listener : mKeyguardChangeListeners) {
listener.onKeyguardVisibilityChanged(visible, occluded, animatingDismiss);
}
@@ -151,17 +175,35 @@ public class ShellController {
@VisibleForTesting
void onKeyguardDismissAnimationFinished() {
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "Keyguard dismiss animation finished");
for (KeyguardChangeListener listener : mKeyguardChangeListeners) {
listener.onKeyguardDismissAnimationFinished();
}
}
+ @VisibleForTesting
+ void onUserChanged(int newUserId, @NonNull Context userContext) {
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "User changed: id=%d", newUserId);
+ for (UserChangeListener listener : mUserChangeListeners) {
+ listener.onUserChanged(newUserId, userContext);
+ }
+ }
+
+ @VisibleForTesting
+ void onUserProfilesChanged(@NonNull List<UserInfo> profiles) {
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "User profiles changed");
+ for (UserChangeListener listener : mUserChangeListeners) {
+ listener.onUserProfilesChanged(profiles);
+ }
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + "mConfigChangeListeners=" + mConfigChangeListeners.size());
pw.println(innerPrefix + "mLastConfiguration=" + mLastConfiguration);
pw.println(innerPrefix + "mKeyguardChangeListeners=" + mKeyguardChangeListeners.size());
+ pw.println(innerPrefix + "mUserChangeListeners=" + mUserChangeListeners.size());
}
/**
@@ -220,5 +262,17 @@ public class ShellController {
mMainExecutor.execute(() ->
ShellController.this.onKeyguardDismissAnimationFinished());
}
+
+ @Override
+ public void onUserChanged(int newUserId, @NonNull Context userContext) {
+ mMainExecutor.execute(() ->
+ ShellController.this.onUserChanged(newUserId, userContext));
+ }
+
+ @Override
+ public void onUserProfilesChanged(@NonNull List<UserInfo> profiles) {
+ mMainExecutor.execute(() ->
+ ShellController.this.onUserProfilesChanged(profiles));
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java
index 254c253b0042..2108c824ac6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java
@@ -16,9 +16,14 @@
package com.android.wm.shell.sysui;
+import android.content.Context;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import androidx.annotation.NonNull;
+
import java.io.PrintWriter;
+import java.util.List;
/**
* General interface for notifying the Shell of common SysUI events like configuration or keyguard
@@ -59,4 +64,14 @@ public interface ShellInterface {
* Notifies the Shell when the keyguard dismiss animation has finished.
*/
default void onKeyguardDismissAnimationFinished() {}
+
+ /**
+ * Notifies the Shell when the user changes.
+ */
+ default void onUserChanged(int newUserId, @NonNull Context userContext) {}
+
+ /**
+ * Notifies the Shell when a profile belonging to the user changes.
+ */
+ default void onUserProfilesChanged(@NonNull List<UserInfo> profiles) {}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/UserChangeListener.java
index 710e5f6eacd3..3d0909f6128d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/UserChangeListener.java
@@ -14,20 +14,26 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline
+package com.android.wm.shell.sysui;
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import android.content.Context;
+import android.content.pm.UserInfo;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
/**
- * A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
- * receives in [emitValue].
+ * Callbacks for when the user or user's profiles changes.
*/
-class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
- private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
- override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
+public interface UserChangeListener {
+ /**
+ * Called when the current (parent) user changes.
+ */
+ default void onUserChanged(int newUserId, @NonNull Context userContext) {}
- suspend fun emitValue(value: RawConnectivityInfo) {
- _rawConnectivityInfoFlow.emit(value)
- }
+ /**
+ * Called when a profile belonging to the user changes.
+ */
+ default void onUserProfilesChanged(@NonNull List<UserInfo> profiles) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
index bdcdb63d2cd6..cc4d268a0000 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl
@@ -34,4 +34,9 @@ interface IShellTransitions {
* Unregisters a remote transition handler.
*/
oneway void unregisterRemote(in RemoteTransition remoteTransition) = 2;
+
+ /**
+ * Retrieves the apply-token used by transactions in Shell
+ */
+ IBinder getShellApplyToken() = 3;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 26d0ec637ccf..d2e8624171f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -219,6 +219,8 @@ public class Transitions implements RemoteCallable<Transitions> {
+ "use ShellInit callbacks to ensure proper ordering");
}
mHandlers.add(handler);
+ // Set initial scale settings.
+ handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
handler.getClass().getSimpleName());
}
@@ -956,6 +958,11 @@ public class Transitions implements RemoteCallable<Transitions> {
transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
});
}
+
+ @Override
+ public IBinder getShellApplyToken() {
+ return SurfaceControl.Transaction.getDefaultApplyToken();
+ }
}
private class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index 0a54b8c016fb..a8154e818a04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -32,7 +32,6 @@ import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
-import com.android.server.wm.traces.common.ComponentMatcher
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 330c9c95e484..cb74315732ab 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -20,6 +20,7 @@ package com.android.wm.shell.flicker
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
import com.android.server.wm.traces.common.IComponentMatcher
import com.android.server.wm.traces.common.region.Region
@@ -94,25 +95,27 @@ fun FlickerTestParameter.layerKeepVisible(
fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- this.isInvisible(component)
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
.then()
- .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- .isVisible(component)
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
.then()
- .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ .splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
}
}
fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ this.splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
.then()
.isVisible(component, true)
.then()
@@ -122,58 +125,96 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible(
fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayersEnd {
- val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(component).coversAtMost(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, endRotation)
- } else {
- getSplitRightBottomRegion(dividerRegion, endRotation)
- }
- )
+ splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, endRotation)
}
}
fun FlickerTestParameter.splitAppLayerBoundsKeepVisible(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, endRotation)
}
}
fun FlickerTestParameter.splitAppLayerBoundsChanges(
component: IComponentMatcher,
- splitLeftTop: Boolean
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean
) {
assertLayers {
- if (splitLeftTop) {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ if (landscapePosLeft) {
+ this.splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
.then()
.isInvisible(component)
.then()
- .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ .splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
} else {
- this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ this.splitAppLayerBoundsSnapToDivider(
+ component, landscapePosLeft, portraitPosTop, endRotation)
}
}
}
fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
component: IComponentMatcher,
- splitLeftTop: Boolean,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean,
rotation: Int
): LayersTraceSubject {
return invoke("splitAppLayerBoundsSnapToDivider") {
- val dividerRegion = it.layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- it.visibleRegion(component).coversAtMost(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, rotation)
+ it.splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, rotation)
+ }
+}
+
+fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
+ component: IComponentMatcher,
+ landscapePosLeft: Boolean,
+ portraitPosTop: Boolean,
+ rotation: Int
+): LayerTraceEntrySubject {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return invoke {
+ val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(component).coversAtMost(
+ if (displayBounds.width > displayBounds.height) {
+ if (landscapePosLeft) {
+ Region.from(
+ 0,
+ 0,
+ (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ displayBounds.bounds.bottom)
+ } else {
+ Region.from(
+ (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
+ 0,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
} else {
- getSplitRightBottomRegion(dividerRegion, rotation)
+ if (portraitPosTop) {
+ Region.from(
+ 0,
+ 0,
+ displayBounds.bounds.right,
+ (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2)
+ } else {
+ Region.from(
+ 0,
+ (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
+ displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
}
)
}
@@ -185,6 +226,10 @@ fun FlickerTestParameter.appWindowBecomesVisible(
assertWm {
this.isAppWindowInvisible(component)
.then()
+ .notContains(component, isOptional = true)
+ .then()
+ .isAppWindowInvisible(component, isOptional = true)
+ .then()
.isAppWindowVisible(component)
}
}
@@ -208,7 +253,7 @@ fun FlickerTestParameter.appWindowIsVisibleAtEnd(
}
fun FlickerTestParameter.appWindowKeepVisible(
- component: IComponentMatcher
+ component: IComponentMatcher
) {
assertWm {
this.isAppWindowVisible(component)
@@ -316,39 +361,3 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
)
}
}
-
-fun getSplitLeftTopRegion(dividerRegion: Region, rotation: Int): Region {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (displayBounds.width > displayBounds.height) {
- Region.from(
- 0,
- 0,
- (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
- displayBounds.bounds.bottom)
- } else {
- Region.from(
- 0,
- 0,
- displayBounds.bounds.right,
- (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2)
- }
-}
-
-fun getSplitRightBottomRegion(dividerRegion: Region, rotation: Int): Region {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- return if (displayBounds.width > displayBounds.height) {
- Region.from(
- (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
- 0,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
- )
- } else {
- Region.from(
- 0,
- (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
- displayBounds.bounds.right,
- displayBounds.bounds.bottom
- )
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 8b717a0cb75e..06361f9ca4ab 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -17,10 +17,10 @@
@file:JvmName("CommonConstants")
package com.android.wm.shell.flicker
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentMatcher("", "AppPairSplitDivider#")
-val DOCKED_STACK_DIVIDER_COMPONENT = ComponentMatcher("", "DockedStackDivider#")
-val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentMatcher("", "StageCoordinatorSplitDivider#")
-val SPLIT_DECOR_MANAGER = ComponentMatcher("", "SplitDecorManager#")
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentNameMatcher("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = ComponentNameMatcher("", "DockedStackDivider#")
+val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentNameMatcher("", "StageCoordinatorSplitDivider#")
+val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index ffbac39c6078..826cc2ef16d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -17,10 +17,10 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
- component: IComponentMatcher
+ component: ComponentNameMatcher
) : BaseAppHelper(instrumentation, activityLabel, component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index c4379e9a27cc..01ba9907c24c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -26,13 +26,13 @@ import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.IComponentNameMatcher
import java.io.IOException
abstract class BaseAppHelper(
instrumentation: Instrumentation,
launcherName: String,
- component: IComponentMatcher
+ component: IComponentNameMatcher
) : StandardAppHelper(
instrumentation,
launcherName,
@@ -46,9 +46,6 @@ abstract class BaseAppHelper(
hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
}
- val defaultWindowName: String
- get() = toWindowName()
-
val ui: UiObject2?
get() = uiDevice.findObject(appSelector)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
index 92b1d216f5ab..245a82f938b3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
@@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
import android.content.Context
import android.provider.Settings
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
class MultiWindowHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: IComponentMatcher
+ componentsInfo: ComponentNameMatcher
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index a1226e682e05..e7f9d9a9d73d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -28,6 +28,7 @@ import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.IComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.SPLIT_DECOR_MANAGER
@@ -37,8 +38,8 @@ import com.android.wm.shell.flicker.testapp.Components
class SplitScreenHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: IComponentMatcher
-) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
+ componentInfo: IComponentNameMatcher
+) : BaseAppHelper(instrumentation, activityLabel, componentInfo) {
companion object {
const val TEST_REPETITIONS = 1
@@ -104,6 +105,25 @@ class SplitScreenHelper(
.waitForAndVerify()
}
+ fun splitFromOverview(tapl: LauncherInstrumentation) {
+ // Note: The initial split position in landscape is different between tablet and phone.
+ // In landscape, tablet will let the first app split to right side, and phone will
+ // split to left side.
+ if (tapl.isTablet) {
+ tapl.workspace.switchToOverview().overviewActions
+ .clickSplit()
+ .currentTask
+ .open()
+ } else {
+ tapl.workspace.switchToOverview().currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
+ }
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
fun dragFromNotificationToSplit(
instrumentation: Instrumentation,
device: UiDevice,
@@ -280,12 +300,12 @@ class SplitScreenHelper(
fun copyContentFromLeftToRight(
instrumentation: Instrumentation,
device: UiDevice,
- sourceApp: IComponentMatcher,
- destinationApp: IComponentMatcher,
+ sourceApp: IComponentNameMatcher,
+ destinationApp: IComponentNameMatcher,
) {
// Copy text from sourceApp
val textView = device.wait(Until.findObject(
- By.res(sourceApp.packageNames.firstOrNull(), "SplitScreenTest")), TIMEOUT_MS)
+ By.res(sourceApp.packageName, "SplitScreenTest")), TIMEOUT_MS)
longPress(instrumentation, textView.getVisibleCenter())
val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
@@ -293,7 +313,7 @@ class SplitScreenHelper(
// Paste text to destinationApp
val editText = device.wait(Until.findObject(
- By.res(destinationApp.packageNames.firstOrNull(), "plain_text_input")), TIMEOUT_MS)
+ By.res(destinationApp.packageName, "plain_text_input")), TIMEOUT_MS)
longPress(instrumentation, editText.getVisibleCenter())
val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 045022414fe0..d194472b9000 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -25,7 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -155,9 +155,9 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec
@Test
fun launcherLayerBecomesVisible() {
testSpec.assertLayers {
- isInvisible(ComponentMatcher.LAUNCHER)
+ isInvisible(ComponentNameMatcher.LAUNCHER)
.then()
- .isVisible(ComponentMatcher.LAUNCHER)
+ .isVisible(ComponentNameMatcher.LAUNCHER)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index dff447b97b9b..507562b00f4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -30,7 +30,6 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 33f787182868..fd1fe65fa3a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -23,7 +23,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.ComponentMatcher.Companion.LAUNCHER
+import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.LAUNCHER
import org.junit.Test
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 5b5b9fc174f6..31a39c190dd6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -24,7 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -63,9 +63,9 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti
val pipCenterY = pipRegion.centerY()
val displayCenterX = device.displayWidth / 2
val barComponent = if (testSpec.isTablet) {
- ComponentMatcher.TASK_BAR
+ ComponentNameMatcher.TASK_BAR
} else {
- ComponentMatcher.NAV_BAR
+ ComponentNameMatcher.NAV_BAR
}
val barLayerHeight = wmHelper.currentState.layerState
.getLayerWithBuffer(barComponent)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 1c0bd0caa901..fd661cfc3ee1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -25,7 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -151,7 +151,7 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition
@Test
fun launcherIsAlwaysVisible() {
testSpec.assertLayers {
- isVisible(ComponentMatcher.LAUNCHER)
+ isVisible(ComponentNameMatcher.LAUNCHER)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 911d402cfde7..454927e57aa2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -28,7 +28,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.Assume.assumeFalse
import org.junit.Before
@@ -105,7 +105,7 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS
@Test
open fun pipIsAboveAppWindow() {
testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(ComponentMatcher.IME, pipApp)
+ isAboveWindow(ComponentNameMatcher.IME, pipApp)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index f69107eae638..d23881475ad6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -92,12 +92,12 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS
@Presubmit
@Test
fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
- primaryApp, splitLeftTop = true)
+ primaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
- textEditApp, splitLeftTop = false)
+ textEditApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index cd92db74af95..ba40c2740bb1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -98,7 +98,7 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen
@Presubmit
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 127ac1e7162b..6828589656d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -96,12 +96,12 @@ class DismissSplitScreenByGoHome(
@Presubmit
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 0f4d98d69c00..9ac7c230096b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -108,12 +108,12 @@ class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(tes
@Presubmit
@Test
fun primaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
/** {@inheritDoc} */
@Postsubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 9564d975194b..8401c1a910b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -94,12 +94,12 @@ class EnterSplitScreenByDragFromAllApps(
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 3b59716180b6..168afda119a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -109,12 +109,12 @@ class EnterSplitScreenByDragFromNotification(
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- sendNotificationApp, splitLeftTop = true)
+ sendNotificationApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 3de98723e132..c1fce5f40b57 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -97,12 +97,12 @@ class EnterSplitScreenByDragFromTaskbar(
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
new file mode 100644
index 000000000000..8cb5d7c24ced
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen from Overview.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ }
+ transitions {
+ SplitScreenHelper.splitFromOverview(tapl)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index bdfd9c7de32f..153056188d24 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -94,12 +94,12 @@ class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreen
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = true)
+ primaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = false)
+ secondaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index da954d97aec2..20544bd2fc2f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -98,12 +98,12 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index db89ff52178b..5a8604f2dccc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -97,12 +97,12 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index c23cdb610671..adea66a49c46 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -99,12 +99,12 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp, splitLeftTop = false)
+ primaryApp, landscapePosLeft = false, portraitPosTop = false)
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, splitLeftTop = true)
+ secondaryApp, landscapePosLeft = true, portraitPosTop = true)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
new file mode 100644
index 000000000000..b2e45a6b3a5c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.activityembedding;
+
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.window.TransitionInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Tests for {@link ActivityEmbeddingAnimationRunner}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:ActivityEmbeddingAnimationRunnerTests
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnimationTestBase {
+
+ @Before
+ public void setup() {
+ super.setUp();
+ doNothing().when(mController).onAnimationFinished(any());
+ }
+
+ @Test
+ public void testStartAnimation() {
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createChange();
+ embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ info.addChange(embeddingChange);
+ doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any());
+
+ mAnimRunner.startAnimation(mTransition, info, mStartTransaction, mFinishTransaction);
+
+ final ArgumentCaptor<Runnable> finishCallback = ArgumentCaptor.forClass(Runnable.class);
+ verify(mAnimRunner).createAnimator(eq(info), eq(mStartTransaction), eq(mFinishTransaction),
+ finishCallback.capture());
+ verify(mStartTransaction).apply();
+ verify(mAnimator).start();
+ verifyNoMoreInteractions(mFinishTransaction);
+ verify(mController, never()).onAnimationFinished(any());
+
+ // Call onAnimationFinished() when the animation is finished.
+ finishCallback.getValue().run();
+
+ verify(mController).onAnimationFinished(mTransition);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
new file mode 100644
index 000000000000..84befdddabdb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.activityembedding;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.mock;
+
+import android.animation.Animator;
+import android.annotation.CallSuper;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** TestBase for ActivityEmbedding animation. */
+abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase {
+
+ @Mock
+ ShellInit mShellInit;
+ @Mock
+ Transitions mTransitions;
+ @Mock
+ IBinder mTransition;
+ @Mock
+ SurfaceControl.Transaction mStartTransaction;
+ @Mock
+ SurfaceControl.Transaction mFinishTransaction;
+ @Mock
+ Transitions.TransitionFinishCallback mFinishCallback;
+ @Mock
+ Animator mAnimator;
+
+ ActivityEmbeddingController mController;
+ ActivityEmbeddingAnimationRunner mAnimRunner;
+ ActivityEmbeddingAnimationSpec mAnimSpec;
+
+ @CallSuper
+ @Before
+ public void setUp() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ MockitoAnnotations.initMocks(this);
+ mController = ActivityEmbeddingController.create(mContext, mShellInit, mTransitions);
+ assertNotNull(mController);
+ mAnimRunner = mController.mAnimationRunner;
+ assertNotNull(mAnimRunner);
+ mAnimSpec = mAnimRunner.mAnimationSpec;
+ assertNotNull(mAnimSpec);
+ spyOn(mController);
+ spyOn(mAnimRunner);
+ spyOn(mAnimSpec);
+ }
+
+ /** Creates a mock {@link TransitionInfo.Change}. */
+ static TransitionInfo.Change createChange() {
+ return new TransitionInfo.Change(mock(WindowContainerToken.class),
+ mock(SurfaceControl.class));
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index bfe3b5468085..cf43b0030d2a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -16,52 +16,117 @@
package com.android.wm.shell.activityembedding;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
-import static org.junit.Assume.assumeTrue;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
-import android.content.Context;
+import android.window.TransitionInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
/**
- * Tests for the activity embedding controller.
+ * Tests for {@link ActivityEmbeddingController}.
*
* Build/Install/Run:
* atest WMShellUnitTests:ActivityEmbeddingControllerTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ActivityEmbeddingControllerTests extends ShellTestCase {
-
- private @Mock Context mContext;
- private @Mock ShellInit mShellInit;
- private @Mock Transitions mTransitions;
- private ActivityEmbeddingController mController;
+public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimationTestBase {
@Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mController = spy(new ActivityEmbeddingController(mContext, mShellInit, mTransitions));
+ public void setup() {
+ super.setUp();
+ doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any());
}
@Test
- public void instantiate_addInitCallback() {
- assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
- verify(mShellInit, times(1)).addInitCallback(any(), any());
+ public void testInstantiate() {
+ verify(mShellInit).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void testOnInit() {
+ mController.onInit();
+
+ verify(mTransitions).addHandler(mController);
+ }
+
+ @Test
+ public void testSetAnimScaleSetting() {
+ mController.setAnimScaleSetting(1.0f);
+
+ verify(mAnimRunner).setAnimScaleSetting(1.0f);
+ verify(mAnimSpec).setAnimScaleSetting(1.0f);
+ }
+
+ @Test
+ public void testStartAnimation_containsNonActivityEmbeddingChange() {
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createChange();
+ embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ final TransitionInfo.Change nonEmbeddingChange = createChange();
+ info.addChange(embeddingChange);
+ info.addChange(nonEmbeddingChange);
+
+ // No-op
+ assertFalse(mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback));
+ verify(mAnimRunner, never()).startAnimation(any(), any(), any(), any());
+ verifyNoMoreInteractions(mStartTransaction);
+ verifyNoMoreInteractions(mFinishTransaction);
+ verifyNoMoreInteractions(mFinishCallback);
+ }
+
+ @Test
+ public void testStartAnimation_onlyActivityEmbeddingChange() {
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createChange();
+ embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ info.addChange(embeddingChange);
+
+ // No-op
+ assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback));
+ verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction);
+ verify(mStartTransaction).apply();
+ verifyNoMoreInteractions(mFinishTransaction);
+ }
+
+ @Test
+ public void testOnAnimationFinished() {
+ // Should not call finish when there is no transition.
+ assertThrows(IllegalStateException.class,
+ () -> mController.onAnimationFinished(mTransition));
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createChange();
+ embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ info.addChange(embeddingChange);
+ mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback);
+
+ verify(mFinishCallback, never()).onTransitionFinished(any(), any());
+ mController.onAnimationFinished(mTransition);
+ verify(mFinishCallback).onTransitionFinished(any(), any());
+
+ // Should not call finish when the finish has already been called.
+ assertThrows(IllegalStateException.class,
+ () -> mController.onAnimationFinished(mTransition));
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 90645ce4747d..cf8297eec061 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -171,6 +171,11 @@ public class OneHandedControllerTest extends OneHandedTestCase {
}
@Test
+ public void testControllerRegistersUserChangeListener() {
+ verify(mMockShellController, times(1)).addUserChangeListener(any());
+ }
+
+ @Test
public void testDefaultShouldNotInOneHanded() {
// Assert default transition state is STATE_NONE
assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 9ed8d84d665f..eb5726bebb74 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -77,9 +78,9 @@ import java.util.Set;
public class PipControllerTest extends ShellTestCase {
private PipController mPipController;
private ShellInit mShellInit;
+ private ShellController mShellController;
@Mock private ShellCommandHandler mMockShellCommandHandler;
- @Mock private ShellController mMockShellController;
@Mock private DisplayController mMockDisplayController;
@Mock private PhonePipMenuController mMockPhonePipMenuController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@@ -110,8 +111,10 @@ public class PipControllerTest extends ShellTestCase {
return null;
}).when(mMockExecutor).execute(any());
mShellInit = spy(new ShellInit(mMockExecutor));
+ mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler,
+ mMockExecutor));
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
- mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+ mShellController, mMockDisplayController, mMockPipAppOpsListener,
mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
@@ -135,12 +138,22 @@ public class PipControllerTest extends ShellTestCase {
@Test
public void instantiatePipController_registerConfigChangeListener() {
- verify(mMockShellController, times(1)).addConfigurationChangeListener(any());
+ verify(mShellController, times(1)).addConfigurationChangeListener(any());
}
@Test
public void instantiatePipController_registerKeyguardChangeListener() {
- verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
+ verify(mShellController, times(1)).addKeyguardChangeListener(any());
+ }
+
+ @Test
+ public void instantiatePipController_registerUserChangeListener() {
+ verify(mShellController, times(1)).addUserChangeListener(any());
+ }
+
+ @Test
+ public void instantiatePipController_registerMediaListener() {
+ verify(mMockPipMediaController, times(1)).registerSessionListenerForCurrentUser();
}
@Test
@@ -167,7 +180,7 @@ public class PipControllerTest extends ShellTestCase {
ShellInit shellInit = new ShellInit(mMockExecutor);
assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
- mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+ mShellController, mMockDisplayController, mMockPipAppOpsListener,
mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
@@ -264,4 +277,11 @@ public class PipControllerTest extends ShellTestCase {
verify(mMockPipBoundsState).setKeepClearAreas(Set.of(keepClearArea), Set.of());
}
+
+ @Test
+ public void onUserChangeRegisterMediaListener() {
+ reset(mMockPipMediaController);
+ mShellController.asShell().onUserChanged(100, mContext);
+ verify(mMockPipMediaController, times(1)).registerSessionListenerForCurrentUser();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index 39e58ffcf9c7..d6ddba9e927d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -17,11 +17,15 @@
package com.android.wm.shell.sysui;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import android.content.Context;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -35,6 +39,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
@SmallTest
@@ -42,22 +48,29 @@ import java.util.Locale;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class ShellControllerTest extends ShellTestCase {
+ private static final int TEST_USER_ID = 100;
+
@Mock
private ShellInit mShellInit;
@Mock
private ShellCommandHandler mShellCommandHandler;
@Mock
private ShellExecutor mExecutor;
+ @Mock
+ private Context mTestUserContext;
private ShellController mController;
private TestConfigurationChangeListener mConfigChangeListener;
private TestKeyguardChangeListener mKeyguardChangeListener;
+ private TestUserChangeListener mUserChangeListener;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mKeyguardChangeListener = new TestKeyguardChangeListener();
mConfigChangeListener = new TestConfigurationChangeListener();
+ mUserChangeListener = new TestUserChangeListener();
mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
mController.onConfigurationChanged(getConfigurationCopy());
}
@@ -68,6 +81,46 @@ public class ShellControllerTest extends ShellTestCase {
}
@Test
+ public void testAddUserChangeListener_ensureCallback() {
+ mController.addUserChangeListener(mUserChangeListener);
+
+ mController.onUserChanged(TEST_USER_ID, mTestUserContext);
+ assertTrue(mUserChangeListener.userChanged == 1);
+ assertTrue(mUserChangeListener.lastUserContext == mTestUserContext);
+ }
+
+ @Test
+ public void testDoubleAddUserChangeListener_ensureSingleCallback() {
+ mController.addUserChangeListener(mUserChangeListener);
+ mController.addUserChangeListener(mUserChangeListener);
+
+ mController.onUserChanged(TEST_USER_ID, mTestUserContext);
+ assertTrue(mUserChangeListener.userChanged == 1);
+ assertTrue(mUserChangeListener.lastUserContext == mTestUserContext);
+ }
+
+ @Test
+ public void testAddRemoveUserChangeListener_ensureNoCallback() {
+ mController.addUserChangeListener(mUserChangeListener);
+ mController.removeUserChangeListener(mUserChangeListener);
+
+ mController.onUserChanged(TEST_USER_ID, mTestUserContext);
+ assertTrue(mUserChangeListener.userChanged == 0);
+ assertTrue(mUserChangeListener.lastUserContext == null);
+ }
+
+ @Test
+ public void testUserProfilesChanged() {
+ mController.addUserChangeListener(mUserChangeListener);
+
+ ArrayList<UserInfo> profiles = new ArrayList<>();
+ profiles.add(mock(UserInfo.class));
+ profiles.add(mock(UserInfo.class));
+ mController.onUserProfilesChanged(profiles);
+ assertTrue(mUserChangeListener.lastUserProfiles.equals(profiles));
+ }
+
+ @Test
public void testAddKeyguardChangeListener_ensureCallback() {
mController.addKeyguardChangeListener(mKeyguardChangeListener);
@@ -332,4 +385,27 @@ public class ShellControllerTest extends ShellTestCase {
dismissAnimationFinished++;
}
}
+
+ private class TestUserChangeListener implements UserChangeListener {
+ // Counts of number of times each of the callbacks are called
+ public int userChanged;
+ public int lastUserId;
+ public Context lastUserContext;
+ public int userProfilesChanged;
+ public List<? extends UserInfo> lastUserProfiles;
+
+
+ @Override
+ public void onUserChanged(int newUserId, @NonNull Context userContext) {
+ userChanged++;
+ lastUserId = newUserId;
+ lastUserContext = userContext;
+ }
+
+ @Override
+ public void onUserProfilesChanged(@NonNull List<UserInfo> profiles) {
+ userProfilesChanged++;
+ lastUserProfiles = profiles;
+ }
+ }
}
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 9a4bda2ee1df..3c67edc9a428 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -125,9 +125,14 @@ Typeface* Typeface::createWithDifferentBaseWeight(Typeface* src, int weight) {
}
Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::FontFamily>>&& families,
- int weight, int italic) {
+ int weight, int italic, const Typeface* fallback) {
Typeface* result = new Typeface;
- result->fFontCollection = minikin::FontCollection::create(families);
+ if (fallback == nullptr) {
+ result->fFontCollection = minikin::FontCollection::create(std::move(families));
+ } else {
+ result->fFontCollection =
+ fallback->fFontCollection->createCollectionWithFamilies(std::move(families));
+ }
if (weight == RESOLVE_BY_FONT_TABLE || italic == RESOLVE_BY_FONT_TABLE) {
int weightFromFont;
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 0c3ef01ab26b..565136e53676 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -78,7 +78,8 @@ public:
Typeface* src, const std::vector<minikin::FontVariation>& variations);
static Typeface* createFromFamilies(
- std::vector<std::shared_ptr<minikin::FontFamily>>&& families, int weight, int italic);
+ std::vector<std::shared_ptr<minikin::FontFamily>>&& families, int weight, int italic,
+ const Typeface* fallback);
static void setDefault(const Typeface* face);
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index f5ed5689e4e8..209b35c5537c 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -109,27 +109,14 @@ static jint Typeface_getWeight(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray,
jlong fallbackPtr, int weight, int italic) {
ScopedLongArrayRO families(env, familyArray);
- std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
Typeface* typeface = (fallbackPtr == 0) ? nullptr : toTypeface(fallbackPtr);
- if (typeface != nullptr) {
- const std::shared_ptr<minikin::FontCollection>& fallbackCollection =
- toTypeface(fallbackPtr)->fFontCollection;
- familyVec.reserve(families.size() + fallbackCollection->getFamilyCount());
- for (size_t i = 0; i < families.size(); i++) {
- FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
- familyVec.emplace_back(family->family);
- }
- for (size_t i = 0; i < fallbackCollection->getFamilyCount(); i++) {
- familyVec.emplace_back(fallbackCollection->getFamilyAt(i));
- }
- } else {
- familyVec.reserve(families.size());
- for (size_t i = 0; i < families.size(); i++) {
- FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
- familyVec.emplace_back(family->family);
- }
+ std::vector<std::shared_ptr<minikin::FontFamily>> familyVec;
+ familyVec.reserve(families.size());
+ for (size_t i = 0; i < families.size(); i++) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]);
+ familyVec.emplace_back(family->family);
}
- return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic));
+ return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic, typeface));
}
// CriticalNative
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 81bcb4e609df..c5196eeccea3 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -54,8 +54,6 @@ typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore
#include <SkColorSpace.h>
#include <SkRefCnt.h>
-class GrVkExtensions;
-
namespace android {
namespace uirenderer {
namespace renderthread {
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 25cc8ca0dafb..499afa039d1f 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -73,7 +73,8 @@ std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const ch
TEST(TypefaceTest, resolveDefault_and_setDefaultTest) {
std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRobotoVariable), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ makeSingleFamlyVector(kRobotoVariable), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ nullptr /* fallback */));
EXPECT_EQ(regular.get(), Typeface::resolveDefault(regular.get()));
// Keep the original to restore it later.
@@ -351,24 +352,24 @@ TEST(TypefaceTest, createAbsolute) {
TEST(TypefaceTest, createFromFamilies_Single) {
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
- std::unique_ptr<Typeface> regular(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 400, false));
+ std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 400, false, nullptr /* fallback */));
EXPECT_EQ(400, regular->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(700).setItalic(false).build();
- std::unique_ptr<Typeface> bold(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 700, false));
+ std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 700, false, nullptr /* fallback */));
EXPECT_EQ(700, bold->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(true).build();
- std::unique_ptr<Typeface> italic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 400, true));
+ std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 400, true, nullptr /* fallback */));
EXPECT_EQ(400, italic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
@@ -376,8 +377,8 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// In Java,
// new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(700).setItalic(true).build();
- std::unique_ptr<Typeface> boldItalic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 700, true));
+ std::unique_ptr<Typeface> boldItalic(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 700, true, nullptr /* fallback */));
EXPECT_EQ(700, boldItalic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
@@ -385,8 +386,8 @@ TEST(TypefaceTest, createFromFamilies_Single) {
// In Java,
// new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(1100).setItalic(false).build();
- std::unique_ptr<Typeface> over1000(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 1100, false));
+ std::unique_ptr<Typeface> over1000(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kRobotoVariable), 1100, false, nullptr /* fallback */));
EXPECT_EQ(1000, over1000->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
@@ -394,30 +395,33 @@ TEST(TypefaceTest, createFromFamilies_Single) {
TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
// In Java, new Typeface.Builder("Family-Regular.ttf").build();
- std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(400, regular->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new Typeface.Builder("Family-Bold.ttf").build();
- std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
- makeSingleFamlyVector(kBoldFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> bold(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kBoldFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(700, bold->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new Typeface.Builder("Family-Italic.ttf").build();
- std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
- makeSingleFamlyVector(kItalicFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> italic(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kItalicFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(400, italic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, new Typeface.Builder("Family-BoldItalic.ttf").build();
- std::unique_ptr<Typeface> boldItalic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kBoldItalicFont),
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> boldItalic(Typeface::createFromFamilies(
+ makeSingleFamlyVector(kBoldItalicFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+ nullptr /* fallback */));
EXPECT_EQ(700, boldItalic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
@@ -427,8 +431,9 @@ TEST(TypefaceTest, createFromFamilies_Family) {
std::vector<std::shared_ptr<minikin::FontFamily>> families = {
buildFamily(kRegularFont), buildFamily(kBoldFont), buildFamily(kItalicFont),
buildFamily(kBoldItalicFont)};
- std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
- std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> typeface(
+ Typeface::createFromFamilies(std::move(families), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(400, typeface->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
@@ -436,10 +441,24 @@ TEST(TypefaceTest, createFromFamilies_Family) {
TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
std::vector<std::shared_ptr<minikin::FontFamily>> families = {
buildFamily(kBoldFont), buildFamily(kItalicFont), buildFamily(kBoldItalicFont)};
- std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
- std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ std::unique_ptr<Typeface> typeface(
+ Typeface::createFromFamilies(std::move(families), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
EXPECT_EQ(700, typeface->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, typeface->fStyle.slant());
}
+TEST(TypefaceTest, createFromFamilies_Family_withFallback) {
+ std::vector<std::shared_ptr<minikin::FontFamily>> fallbackFamilies = {
+ buildFamily(kBoldFont), buildFamily(kItalicFont), buildFamily(kBoldItalicFont)};
+ std::unique_ptr<Typeface> fallback(
+ Typeface::createFromFamilies(std::move(fallbackFamilies), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, nullptr /* fallback */));
+ std::unique_ptr<Typeface> regular(
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE,
+ RESOLVE_BY_FONT_TABLE, fallback.get()));
+ EXPECT_EQ(400, regular->fStyle.weight());
+ EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
+}
+
} // namespace
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 8afc7d999d2e..b6f07f43cef5 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -79,8 +79,9 @@ public final class MediaRouter2Manager {
final String mPackageName;
private final Context mContext;
- @GuardedBy("sLock")
- private Client mClient;
+
+ private final Client mClient;
+
private final IMediaRouterService mMediaRouterService;
private final AtomicInteger mScanRequestCount = new AtomicInteger(/* initialValue= */ 0);
final Handler mHandler;
@@ -120,7 +121,12 @@ public final class MediaRouter2Manager {
.getSystemService(Context.MEDIA_SESSION_SERVICE);
mPackageName = mContext.getPackageName();
mHandler = new Handler(context.getMainLooper());
- mHandler.post(this::getOrCreateClient);
+ mClient = new Client();
+ try {
+ mMediaRouterService.registerManager(mClient, mPackageName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
}
/**
@@ -167,7 +173,7 @@ public final class MediaRouter2Manager {
public void registerScanRequest() {
if (mScanRequestCount.getAndIncrement() == 0) {
try {
- mMediaRouterService.startScan(getOrCreateClient());
+ mMediaRouterService.startScan(mClient);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -194,7 +200,7 @@ public final class MediaRouter2Manager {
})
== 0) {
try {
- mMediaRouterService.stopScan(getOrCreateClient());
+ mMediaRouterService.stopScan(mClient);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -358,8 +364,7 @@ public final class MediaRouter2Manager {
@Nullable
public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) {
try {
- return mMediaRouterService.getSystemSessionInfoForPackage(
- getOrCreateClient(), packageName);
+ return mMediaRouterService.getSystemSessionInfoForPackage(mClient, packageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -424,7 +429,7 @@ public final class MediaRouter2Manager {
@NonNull
public List<RoutingSessionInfo> getRemoteSessions() {
try {
- return mMediaRouterService.getRemoteSessions(getOrCreateClient());
+ return mMediaRouterService.getRemoteSessions(mClient);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -512,8 +517,7 @@ public final class MediaRouter2Manager {
try {
int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.setRouteVolumeWithManager(
- getOrCreateClient(), requestId, route, volume);
+ mMediaRouterService.setRouteVolumeWithManager(mClient, requestId, route, volume);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -540,7 +544,7 @@ public final class MediaRouter2Manager {
try {
int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.setSessionVolumeWithManager(
- getOrCreateClient(), requestId, sessionInfo.getId(), volume);
+ mClient, requestId, sessionInfo.getId(), volume);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -765,7 +769,7 @@ public final class MediaRouter2Manager {
try {
int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.selectRouteWithManager(
- getOrCreateClient(), requestId, sessionInfo.getId(), route);
+ mClient, requestId, sessionInfo.getId(), route);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -804,7 +808,7 @@ public final class MediaRouter2Manager {
try {
int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.deselectRouteWithManager(
- getOrCreateClient(), requestId, sessionInfo.getId(), route);
+ mClient, requestId, sessionInfo.getId(), route);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -825,8 +829,7 @@ public final class MediaRouter2Manager {
try {
int requestId = mNextRequestId.getAndIncrement();
- mMediaRouterService.releaseSessionWithManager(
- getOrCreateClient(), requestId, sessionInfo.getId());
+ mMediaRouterService.releaseSessionWithManager(mClient, requestId, sessionInfo.getId());
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -843,7 +846,7 @@ public final class MediaRouter2Manager {
try {
mMediaRouterService.transferToRouteWithManager(
- getOrCreateClient(), requestId, session.getId(), route);
+ mClient, requestId, session.getId(), route);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -860,7 +863,7 @@ public final class MediaRouter2Manager {
try {
mMediaRouterService.requestCreateSessionWithManager(
- getOrCreateClient(), requestId, oldSession, route);
+ mClient, requestId, oldSession, route);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -906,22 +909,6 @@ public final class MediaRouter2Manager {
sessionInfo.getOwnerPackageName());
}
- private Client getOrCreateClient() {
- synchronized (sLock) {
- if (mClient != null) {
- return mClient;
- }
- Client client = new Client();
- try {
- mMediaRouterService.registerManager(client, mPackageName);
- mClient = client;
- return client;
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
- }
-
/**
* Interface for receiving events about media routing changes.
*/
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 4df745ff6a20..30d0c35bcbb0 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -245,32 +245,23 @@ ASystemFontIterator* ASystemFontIterator_open() {
std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
std::unordered_set<AFont, FontHasher> fonts;
- minikin::SystemFonts::getFontMap(
- [&fonts](const std::vector<std::shared_ptr<minikin::FontCollection>>& collections) {
- for (const auto& fc : collections) {
- for (uint32_t i = 0; i < fc->getFamilyCount(); ++i) {
- const auto& family = fc->getFamilyAt(i);
- for (uint32_t j = 0; j < family->getNumFonts(); ++j) {
- const minikin::Font* font = family->getFont(j);
-
- std::optional<std::string> locale;
- uint32_t localeId = font->getLocaleListId();
- if (localeId != minikin::kEmptyLocaleListId) {
- locale.emplace(minikin::getLocaleString(localeId));
- }
- std::vector<std::pair<uint32_t, float>> axes;
- for (const auto& [tag, value] : font->typeface()->GetAxes()) {
- axes.push_back(std::make_pair(tag, value));
- }
-
- fonts.insert(
- {font->typeface()->GetFontPath(), std::move(locale),
- font->style().weight(),
- font->style().slant() == minikin::FontStyle::Slant::ITALIC,
- static_cast<uint32_t>(font->typeface()->GetFontIndex()),
- axes});
- }
+ minikin::SystemFonts::getFontSet(
+ [&fonts](const std::vector<std::shared_ptr<minikin::Font>>& fontSet) {
+ for (const auto& font : fontSet) {
+ std::optional<std::string> locale;
+ uint32_t localeId = font->getLocaleListId();
+ if (localeId != minikin::kEmptyLocaleListId) {
+ locale.emplace(minikin::getLocaleString(localeId));
}
+ std::vector<std::pair<uint32_t, float>> axes;
+ for (const auto& [tag, value] : font->typeface()->GetAxes()) {
+ axes.push_back(std::make_pair(tag, value));
+ }
+
+ fonts.insert({font->typeface()->GetFontPath(), std::move(locale),
+ font->style().weight(),
+ font->style().slant() == minikin::FontStyle::Slant::ITALIC,
+ static_cast<uint32_t>(font->typeface()->GetFontIndex()), axes});
}
});
diff --git a/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml b/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml
index cb757d34dfb4..9578fcf0f4cd 100644
--- a/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml
+++ b/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml
@@ -1,6 +1,15 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
+ <option name="PACKAGES_TO_USE_STAR_IMPORTS">
+ <value />
+ </option>
+ <option name="PACKAGES_IMPORT_LAYOUT">
+ <value>
+ <package name="" alias="false" withSubpackages="true" />
+ <package name="" alias="true" withSubpackages="true" />
+ </value>
+ </option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
</JetCodeStyleSettings>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
index 937e59493632..36361ddfe08a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
@@ -17,7 +17,6 @@
package com.android.settingslib.spa.gallery.page
import android.os.Bundle
-import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavType
@@ -28,12 +27,14 @@ import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+private const val TITLE = "Sample page with arguments"
private const val STRING_PARAM_NAME = "stringParam"
private const val INT_PARAM_NAME = "intParam"
object ArgumentPageProvider : SettingsPageProvider {
- override val name = Destinations.Argument
+ override val name = "Argument"
override val arguments = listOf(
navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
@@ -51,17 +52,17 @@ object ArgumentPageProvider : SettingsPageProvider {
@Composable
fun EntryItem(stringParam: String, intParam: Int) {
Preference(object : PreferenceModel {
- override val title = "Sample page with arguments"
+ override val title = TITLE
override val summary =
"$STRING_PARAM_NAME=$stringParam, $INT_PARAM_NAME=$intParam".toState()
- override val onClick = navigator("${Destinations.Argument}/$stringParam/$intParam")
+ override val onClick = navigator("$name/$stringParam/$intParam")
})
}
}
@Composable
fun ArgumentPage(stringParam: String, intParam: Int) {
- Column {
+ RegularScaffold(title = TITLE) {
Preference(object : PreferenceModel {
override val title = "String param value"
override val summary = stringParam.toState()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt
index 143c365d3a74..82005ec44c74 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/FooterPage.kt
@@ -17,12 +17,8 @@
package com.android.settingslib.spa.gallery.page
import android.os.Bundle
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
@@ -30,8 +26,11 @@ import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Footer
+private const val TITLE = "Sample Footer"
+
object FooterPageProvider : SettingsPageProvider {
override val name = "Footer"
@@ -43,7 +42,7 @@ object FooterPageProvider : SettingsPageProvider {
@Composable
fun EntryItem() {
Preference(object : PreferenceModel {
- override val title = "Sample Footer"
+ override val title = TITLE
override val onClick = navigator(name)
})
}
@@ -51,7 +50,7 @@ object FooterPageProvider : SettingsPageProvider {
@Composable
private fun FooterPage() {
- Column(Modifier.verticalScroll(rememberScrollState())) {
+ RegularScaffold(title = TITLE) {
Preference(remember {
object : PreferenceModel {
override val title = "Some Preference"
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/HomePage.kt
index ee077f4a25e6..6b7de8d29d0f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/HomePage.kt
@@ -31,7 +31,7 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
object HomePageProvider : SettingsPageProvider {
- override val name = Destinations.Home
+ override val name = "Home"
@Composable
override fun Page(arguments: Bundle?) {
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PageRepository.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PageRepository.kt
index 64652257c20b..cbfc60395d69 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PageRepository.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PageRepository.kt
@@ -18,14 +18,6 @@ package com.android.settingslib.spa.gallery.page
import com.android.settingslib.spa.framework.api.SettingsPageRepository
-object Destinations {
- const val Home = "Home"
- const val Preference = "Preference"
- const val SwitchPreference = "SwitchPreference"
- const val Argument = "Argument"
- const val Slider = "Slider"
-}
-
val galleryPageRepository = SettingsPageRepository(
allPages = listOf(
HomePageProvider,
@@ -36,5 +28,5 @@ val galleryPageRepository = SettingsPageRepository(
SettingsPagerPageProvider,
FooterPageProvider,
),
- startDestination = Destinations.Home,
+ startDestination = HomePageProvider.name,
)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt
index 8a29d35ad9d9..0463e58f09f6 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/PreferencePage.kt
@@ -17,9 +17,6 @@
package com.android.settingslib.spa.gallery.page
import android.os.Bundle
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.material.icons.outlined.TouchApp
@@ -31,7 +28,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
@@ -39,11 +35,14 @@ import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.SettingsIcon
import kotlinx.coroutines.delay
+private const val TITLE = "Sample Preference"
+
object PreferencePageProvider : SettingsPageProvider {
- override val name = Destinations.Preference
+ override val name = "Preference"
@Composable
override fun Page(arguments: Bundle?) {
@@ -53,15 +52,15 @@ object PreferencePageProvider : SettingsPageProvider {
@Composable
fun EntryItem() {
Preference(object : PreferenceModel {
- override val title = "Sample Preference"
- override val onClick = navigator(Destinations.Preference)
+ override val title = TITLE
+ override val onClick = navigator(name)
})
}
}
@Composable
private fun PreferencePage() {
- Column(Modifier.verticalScroll(rememberScrollState())) {
+ RegularScaffold(title = TITLE) {
Preference(object : PreferenceModel {
override val title = "Preference"
})
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
index 5351ea65da47..df48517fb1fb 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
@@ -17,11 +17,7 @@
package com.android.settingslib.spa.gallery.page
import android.os.Bundle
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
@@ -29,7 +25,10 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.SettingsPager
-import com.android.settingslib.spa.widget.ui.SettingsTitle
+import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
+import com.android.settingslib.spa.widget.ui.PlaceholderTitle
+
+private const val TITLE = "Sample SettingsPager"
object SettingsPagerPageProvider : SettingsPageProvider {
override val name = "SettingsPager"
@@ -42,7 +41,7 @@ object SettingsPagerPageProvider : SettingsPageProvider {
@Composable
fun EntryItem() {
Preference(object : PreferenceModel {
- override val title = "Sample SettingsPager"
+ override val title = TITLE
override val onClick = navigator(name)
})
}
@@ -50,9 +49,9 @@ object SettingsPagerPageProvider : SettingsPageProvider {
@Composable
private fun SettingsPagerPage() {
- SettingsPager(listOf("Personal", "Work")) {
- Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
- SettingsTitle(title = "Page $it")
+ SettingsScaffold(title = TITLE) {
+ SettingsPager(listOf("Personal", "Work")) {
+ PlaceholderTitle("Page $it")
}
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
index 9bcac1b84a0b..04046fa18df1 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
@@ -17,9 +17,6 @@
package com.android.settingslib.spa.gallery.page
import android.os.Bundle
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccessAlarm
import androidx.compose.material.icons.outlined.MusicNote
@@ -29,18 +26,20 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.SettingsSlider
import com.android.settingslib.spa.widget.ui.SettingsSliderModel
+private const val TITLE = "Sample Slider"
+
object SliderPageProvider : SettingsPageProvider {
- override val name = Destinations.Slider
+ override val name = "Slider"
@Composable
override fun Page(arguments: Bundle?) {
@@ -50,15 +49,15 @@ object SliderPageProvider : SettingsPageProvider {
@Composable
fun EntryItem() {
Preference(object : PreferenceModel {
- override val title = "Sample Slider"
- override val onClick = navigator(Destinations.Slider)
+ override val title = TITLE
+ override val onClick = navigator(name)
})
}
}
@Composable
private fun SliderPage() {
- Column(Modifier.verticalScroll(rememberScrollState())) {
+ RegularScaffold(title = TITLE) {
SettingsSlider(object : SettingsSliderModel {
override val title = "Slider"
override val initValue = 40
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt
index b6f725821728..e9e5d356d2d4 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SwitchPreferencePage.kt
@@ -17,15 +17,11 @@
package com.android.settingslib.spa.gallery.page
import android.os.Bundle
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
@@ -35,10 +31,13 @@ import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import kotlinx.coroutines.delay
+private const val TITLE = "Sample SwitchPreference"
+
object SwitchPreferencePageProvider : SettingsPageProvider {
- override val name = Destinations.SwitchPreference
+ override val name = "SwitchPreference"
@Composable
override fun Page(arguments: Bundle?) {
@@ -48,15 +47,15 @@ object SwitchPreferencePageProvider : SettingsPageProvider {
@Composable
fun EntryItem() {
Preference(object : PreferenceModel {
- override val title = "Sample SwitchPreference"
- override val onClick = navigator(Destinations.SwitchPreference)
+ override val title = TITLE
+ override val onClick = navigator(name)
})
}
}
@Composable
private fun SwitchPreferencePage() {
- Column(Modifier.verticalScroll(rememberScrollState())) {
+ RegularScaffold(title = TITLE) {
SampleSwitchPreference()
SampleSwitchPreferenceWithSummary()
SampleSwitchPreferenceWithAsyncSummary()
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LogCompositions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LogCompositions.kt
new file mode 100644
index 000000000000..4eef2a8ffbbf
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LogCompositions.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.compose
+
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.remember
+
+const val ENABLE_LOG_COMPOSITIONS = false
+
+data class LogCompositionsRef(var count: Int)
+
+// Note the inline function below which ensures that this function is essentially
+// copied at the call site to ensure that its logging only recompositions from the
+// original call site.
+@Suppress("NOTHING_TO_INLINE")
+@Composable
+inline fun LogCompositions(tag: String, msg: String) {
+ if (ENABLE_LOG_COMPOSITIONS) {
+ val ref = remember { LogCompositionsRef(0) }
+ SideEffect { ref.count++ }
+ Log.d(tag, "Compositions $msg: ${ref.count}")
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Pager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Pager.kt
new file mode 100644
index 000000000000..bf338574c42d
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Pager.kt
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.compose
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.filter
+
+/**
+ * *************************************************************************************************
+ * This file was forked from
+ * https://github.com/google/accompanist/blob/main/pager/src/main/java/com/google/accompanist/pager/Pager.kt
+ * and will be removed once it lands in AndroidX.
+ */
+
+/**
+ * A horizontally scrolling layout that allows users to flip between items to the left and right.
+ *
+ * @sample com.google.accompanist.sample.pager.HorizontalPagerSample
+ *
+ * @param count the number of pages.
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the pager's state.
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the end to the start and [PagerState.currentPage] == 0 will mean
+ * the first item is located at the end.
+ * @param itemSpacing horizontal spacing to add between items.
+ * @param key the scroll position will be maintained based on the key, which means if you
+ * add/remove items before the current visible item the item with the given key will be kept as the
+ * first visible one.
+ * @param content a block which describes the content. Inside this block you can reference
+ * [PagerScope.currentPage] and other properties in [PagerScope].
+ */
+@Composable
+fun HorizontalPager(
+ count: Int,
+ modifier: Modifier = Modifier,
+ state: PagerState = rememberPagerState(),
+ reverseLayout: Boolean = false,
+ itemSpacing: Dp = 0.dp,
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+ key: ((page: Int) -> Any)? = null,
+ content: @Composable PagerScope.(page: Int) -> Unit,
+) {
+ Pager(
+ count = count,
+ state = state,
+ modifier = modifier,
+ isVertical = false,
+ reverseLayout = reverseLayout,
+ itemSpacing = itemSpacing,
+ verticalAlignment = verticalAlignment,
+ key = key,
+ contentPadding = contentPadding,
+ content = content
+ )
+}
+
+/**
+ * A vertically scrolling layout that allows users to flip between items to the top and bottom.
+ *
+ * @sample com.google.accompanist.sample.pager.VerticalPagerSample
+ *
+ * @param count the number of pages.
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the pager's state.
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean
+ * the first item is located at the bottom.
+ * @param itemSpacing vertical spacing to add between items.
+ * @param key the scroll position will be maintained based on the key, which means if you
+ * add/remove items before the current visible item the item with the given key will be kept as the
+ * first visible one.
+ * @param content a block which describes the content. Inside this block you can reference
+ * [PagerScope.currentPage] and other properties in [PagerScope].
+ */
+@Composable
+fun VerticalPager(
+ count: Int,
+ modifier: Modifier = Modifier,
+ state: PagerState = rememberPagerState(),
+ reverseLayout: Boolean = false,
+ itemSpacing: Dp = 0.dp,
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ key: ((page: Int) -> Any)? = null,
+ content: @Composable() (PagerScope.(page: Int) -> Unit),
+) {
+ Pager(
+ count = count,
+ state = state,
+ modifier = modifier,
+ isVertical = true,
+ reverseLayout = reverseLayout,
+ itemSpacing = itemSpacing,
+ horizontalAlignment = horizontalAlignment,
+ key = key,
+ contentPadding = contentPadding,
+ content = content
+ )
+}
+
+@Composable
+internal fun Pager(
+ count: Int,
+ modifier: Modifier,
+ state: PagerState,
+ reverseLayout: Boolean,
+ itemSpacing: Dp,
+ isVertical: Boolean,
+ key: ((page: Int) -> Any)?,
+ contentPadding: PaddingValues,
+ verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ content: @Composable PagerScope.(page: Int) -> Unit,
+) {
+ require(count >= 0) { "pageCount must be >= 0" }
+
+ LaunchedEffect(count) {
+ state.currentPage = minOf(count - 1, state.currentPage).coerceAtLeast(0)
+ }
+
+ // Once a fling (scroll) has finished, notify the state
+ LaunchedEffect(state) {
+ // When a 'scroll' has finished, notify the state
+ snapshotFlow { state.isScrollInProgress }
+ .filter { !it }
+ // initially isScrollInProgress is false as well and we want to start receiving
+ // the events only after the real scroll happens.
+ .drop(1)
+ .collect { state.onScrollFinished() }
+ }
+ LaunchedEffect(state) {
+ snapshotFlow { state.mostVisiblePageLayoutInfo?.index }
+ .distinctUntilChanged()
+ .collect { state.updateCurrentPageBasedOnLazyListState() }
+ }
+ val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
+ LaunchedEffect(density, contentPadding, isVertical, layoutDirection, reverseLayout, state) {
+ with(density) {
+ // this should be exposed on LazyListLayoutInfo instead. b/200920410
+ state.afterContentPadding = if (isVertical) {
+ if (!reverseLayout) {
+ contentPadding.calculateBottomPadding()
+ } else {
+ contentPadding.calculateTopPadding()
+ }
+ } else {
+ if (!reverseLayout) {
+ contentPadding.calculateEndPadding(layoutDirection)
+ } else {
+ contentPadding.calculateStartPadding(layoutDirection)
+ }
+ }.roundToPx()
+ }
+ }
+
+ val pagerScope = remember(state) { PagerScopeImpl(state) }
+
+ // We only consume nested flings in the main-axis, allowing cross-axis flings to propagate
+ // as normal
+ val consumeFlingNestedScrollConnection = remember(isVertical) {
+ ConsumeFlingNestedScrollConnection(
+ consumeHorizontal = !isVertical,
+ consumeVertical = isVertical,
+ )
+ }
+
+ if (isVertical) {
+ LazyColumn(
+ state = state.lazyListState,
+ verticalArrangement = Arrangement.spacedBy(itemSpacing, verticalAlignment),
+ horizontalAlignment = horizontalAlignment,
+ reverseLayout = reverseLayout,
+ contentPadding = contentPadding,
+ modifier = modifier,
+ ) {
+ items(
+ count = count,
+ key = key,
+ ) { page ->
+ Box(
+ Modifier
+ // We don't any nested flings to continue in the pager, so we add a
+ // connection which consumes them.
+ // See: https://github.com/google/accompanist/issues/347
+ .nestedScroll(connection = consumeFlingNestedScrollConnection)
+ // Constraint the content height to be <= than the height of the pager.
+ .fillParentMaxHeight()
+ .wrapContentSize()
+ ) {
+ pagerScope.content(page)
+ }
+ }
+ }
+ } else {
+ LazyRow(
+ state = state.lazyListState,
+ verticalAlignment = verticalAlignment,
+ horizontalArrangement = Arrangement.spacedBy(itemSpacing, horizontalAlignment),
+ reverseLayout = reverseLayout,
+ contentPadding = contentPadding,
+ modifier = modifier,
+ ) {
+ items(
+ count = count,
+ key = key,
+ ) { page ->
+ Box(
+ Modifier
+ // We don't any nested flings to continue in the pager, so we add a
+ // connection which consumes them.
+ // See: https://github.com/google/accompanist/issues/347
+ .nestedScroll(connection = consumeFlingNestedScrollConnection)
+ // Constraint the content width to be <= than the width of the pager.
+ .fillParentMaxWidth()
+ .wrapContentSize()
+ ) {
+ pagerScope.content(page)
+ }
+ }
+ }
+ }
+}
+
+private class ConsumeFlingNestedScrollConnection(
+ private val consumeHorizontal: Boolean,
+ private val consumeVertical: Boolean,
+) : NestedScrollConnection {
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource
+ ): Offset = when (source) {
+ // We can consume all resting fling scrolls so that they don't propagate up to the
+ // Pager
+ NestedScrollSource.Fling -> available.consume(consumeHorizontal, consumeVertical)
+ else -> Offset.Zero
+ }
+
+ override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ // We can consume all post fling velocity on the main-axis
+ // so that it doesn't propagate up to the Pager
+ return available.consume(consumeHorizontal, consumeVertical)
+ }
+}
+
+private fun Offset.consume(
+ consumeHorizontal: Boolean,
+ consumeVertical: Boolean,
+): Offset = Offset(
+ x = if (consumeHorizontal) this.x else 0f,
+ y = if (consumeVertical) this.y else 0f,
+)
+
+private fun Velocity.consume(
+ consumeHorizontal: Boolean,
+ consumeVertical: Boolean,
+): Velocity = Velocity(
+ x = if (consumeHorizontal) this.x else 0f,
+ y = if (consumeVertical) this.y else 0f,
+)
+
+/**
+ * Scope for [HorizontalPager] content.
+ */
+@Stable
+interface PagerScope {
+ /**
+ * Returns the current selected page
+ */
+ val currentPage: Int
+
+ /**
+ * The current offset from the start of [currentPage], as a ratio of the page width.
+ */
+ val currentPageOffset: Float
+}
+
+private class PagerScopeImpl(
+ private val state: PagerState,
+) : PagerScope {
+ override val currentPage: Int get() = state.currentPage
+ override val currentPageOffset: Float get() = state.currentPageOffset
+}
+
+/**
+ * Calculate the offset for the given [page] from the current scroll position. This is useful
+ * when using the scroll position to apply effects or animations to items.
+ *
+ * The returned offset can positive or negative, depending on whether which direction the [page] is
+ * compared to the current scroll position.
+ *
+ * @sample com.google.accompanist.sample.pager.HorizontalPagerWithOffsetTransition
+ */
+fun PagerScope.calculateCurrentOffsetForPage(page: Int): Float {
+ return (currentPage - page) + currentPageOffset
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PagerState.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PagerState.kt
new file mode 100644
index 000000000000..21ba11739af0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/PagerState.kt
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.compose
+
+import androidx.annotation.FloatRange
+import androidx.annotation.IntRange
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.lazy.LazyListItemInfo
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.Saver
+import androidx.compose.runtime.saveable.listSaver
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+import kotlin.math.roundToInt
+
+/**
+ * *************************************************************************************************
+ * This file was forked from
+ * https://github.com/google/accompanist/blob/main/pager/src/main/java/com/google/accompanist/pager/PagerState.kt
+ * and will be removed once it lands in AndroidX.
+ */
+
+/**
+ * Creates a [PagerState] that is remembered across compositions.
+ *
+ * Changes to the provided values for [initialPage] will **not** result in the state being
+ * recreated or changed in any way if it has already
+ * been created.
+ *
+ * @param initialPage the initial value for [PagerState.currentPage]
+ */
+@Composable
+fun rememberPagerState(
+ @IntRange(from = 0) initialPage: Int = 0,
+): PagerState = rememberSaveable(saver = PagerState.Saver) {
+ PagerState(
+ currentPage = initialPage,
+ )
+}
+
+/**
+ * A state object that can be hoisted to control and observe scrolling for [HorizontalPager].
+ *
+ * In most cases, this will be created via [rememberPagerState].
+ *
+ * @param currentPage the initial value for [PagerState.currentPage]
+ */
+@Stable
+class PagerState(
+ @IntRange(from = 0) currentPage: Int = 0,
+) : ScrollableState {
+ // Should this be public?
+ internal val lazyListState = LazyListState(firstVisibleItemIndex = currentPage)
+
+ private var _currentPage by mutableStateOf(currentPage)
+
+ // finds the page which has larger visible area within the viewport not including paddings
+ internal val mostVisiblePageLayoutInfo: LazyListItemInfo?
+ get() {
+ val layoutInfo = lazyListState.layoutInfo
+ return layoutInfo.visibleItemsInfo.maxByOrNull {
+ val start = maxOf(it.offset, 0)
+ val end = minOf(
+ it.offset + it.size, layoutInfo.viewportEndOffset - afterContentPadding)
+ end - start
+ }
+ }
+
+ internal var afterContentPadding = 0
+
+ private val currentPageLayoutInfo: LazyListItemInfo?
+ get() = lazyListState.layoutInfo.visibleItemsInfo.lastOrNull {
+ it.index == currentPage
+ }
+
+ /**
+ * [InteractionSource] that will be used to dispatch drag events when this
+ * list is being dragged. If you want to know whether the fling (or animated scroll) is in
+ * progress, use [isScrollInProgress].
+ */
+ val interactionSource: InteractionSource
+ get() = lazyListState.interactionSource
+
+ /**
+ * The number of pages to display.
+ */
+ @get:IntRange(from = 0)
+ val pageCount: Int by derivedStateOf {
+ lazyListState.layoutInfo.totalItemsCount
+ }
+
+ /**
+ * The index of the currently selected page. This may not be the page which is
+ * currently displayed on screen.
+ *
+ * To update the scroll position, use [scrollToPage] or [animateScrollToPage].
+ */
+ @get:IntRange(from = 0)
+ var currentPage: Int
+ get() = _currentPage
+ internal set(value) {
+ if (value != _currentPage) {
+ _currentPage = value
+ }
+ }
+
+ /**
+ * The current offset from the start of [currentPage], as a ratio of the page width.
+ *
+ * To update the scroll position, use [scrollToPage] or [animateScrollToPage].
+ */
+ val currentPageOffset: Float by derivedStateOf {
+ currentPageLayoutInfo?.let {
+ // We coerce since itemSpacing can make the offset > 1f.
+ // We don't want to count spacing in the offset so cap it to 1f
+ (-it.offset / it.size.toFloat()).coerceIn(-1f, 1f)
+ } ?: 0f
+ }
+
+ /**
+ * The target page for any on-going animations.
+ */
+ private var animationTargetPage: Int? by mutableStateOf(null)
+
+ /**
+ * Animate (smooth scroll) to the given page to the middle of the viewport.
+ *
+ * Cancels the currently running scroll, if any, and suspends until the cancellation is
+ * complete.
+ *
+ * @param page the page to animate to. Must be >= 0.
+ * @param pageOffset the percentage of the page size to offset, from the start of [page].
+ * Must be in the range -1f..1f.
+ */
+ suspend fun animateScrollToPage(
+ @IntRange(from = 0) page: Int,
+ @FloatRange(from = -1.0, to = 1.0) pageOffset: Float = 0f,
+ ) {
+ requireCurrentPage(page, "page")
+ requireCurrentPageOffset(pageOffset, "pageOffset")
+ try {
+ animationTargetPage = page
+
+ // pre-jump to nearby item for long jumps as an optimization
+ // the same trick is done in ViewPager2
+ val oldPage = lazyListState.firstVisibleItemIndex
+ if (abs(page - oldPage) > 3) {
+ lazyListState.scrollToItem(if (page > oldPage) page - 3 else page + 3)
+ }
+
+ if (pageOffset.absoluteValue <= 0.005f) {
+ // If the offset is (close to) zero, just call animateScrollToItem and we're done
+ lazyListState.animateScrollToItem(index = page)
+ } else {
+ // Else we need to figure out what the offset is in pixels...
+ lazyListState.scroll { } // this will await for the first layout.
+ val layoutInfo = lazyListState.layoutInfo
+ var target = layoutInfo.visibleItemsInfo
+ .firstOrNull { it.index == page }
+
+ if (target != null) {
+ // If we have access to the target page layout, we can calculate the pixel
+ // offset from the size
+ lazyListState.animateScrollToItem(
+ index = page,
+ scrollOffset = (target.size * pageOffset).roundToInt()
+ )
+ } else if (layoutInfo.visibleItemsInfo.isNotEmpty()) {
+ // If we don't, we use the current page size as a guide
+ val currentSize = layoutInfo.visibleItemsInfo.first().size
+ lazyListState.animateScrollToItem(
+ index = page,
+ scrollOffset = (currentSize * pageOffset).roundToInt()
+ )
+
+ // The target should be visible now
+ target = lazyListState.layoutInfo.visibleItemsInfo.firstOrNull {
+ it.index == page
+ }
+
+ if (target != null && target.size != currentSize) {
+ // If the size we used for calculating the offset differs from the actual
+ // target page size, we need to scroll again. This doesn't look great,
+ // but there's not much else we can do.
+ lazyListState.animateScrollToItem(
+ index = page,
+ scrollOffset = (target.size * pageOffset).roundToInt()
+ )
+ }
+ }
+ }
+ } finally {
+ // We need to manually call this, as the `animateScrollToItem` call above will happen
+ // in 1 frame, which is usually too fast for the LaunchedEffect in Pager to detect
+ // the change. This is especially true when running unit tests.
+ onScrollFinished()
+ }
+ }
+
+ /**
+ * Instantly brings the item at [page] to the middle of the viewport.
+ *
+ * Cancels the currently running scroll, if any, and suspends until the cancellation is
+ * complete.
+ *
+ * @param page the page to snap to. Must be >= 0.
+ * @param pageOffset the percentage of the page size to offset, from the start of [page].
+ * Must be in the range -1f..1f.
+ */
+ suspend fun scrollToPage(
+ @IntRange(from = 0) page: Int,
+ @FloatRange(from = -1.0, to = 1.0) pageOffset: Float = 0f,
+ ) {
+ requireCurrentPage(page, "page")
+ requireCurrentPageOffset(pageOffset, "pageOffset")
+ try {
+ animationTargetPage = page
+
+ // First scroll to the given page. It will now be laid out at offset 0
+ lazyListState.scrollToItem(index = page)
+ updateCurrentPageBasedOnLazyListState()
+
+ // If we have a start spacing, we need to offset (scroll) by that too
+ if (pageOffset.absoluteValue > 0.0001f) {
+ currentPageLayoutInfo?.let {
+ scroll {
+ scrollBy(it.size * pageOffset)
+ }
+ }
+ }
+ } finally {
+ // We need to manually call this, as the `scroll` call above will happen in 1 frame,
+ // which is usually too fast for the LaunchedEffect in Pager to detect the change.
+ // This is especially true when running unit tests.
+ onScrollFinished()
+ }
+ }
+
+ internal fun updateCurrentPageBasedOnLazyListState() {
+ // Then update the current page to our layout page
+ mostVisiblePageLayoutInfo?.let {
+ currentPage = it.index
+ }
+ }
+
+ internal fun onScrollFinished() {
+ // Clear the animation target page
+ animationTargetPage = null
+ }
+
+ override suspend fun scroll(
+ scrollPriority: MutatePriority,
+ block: suspend ScrollScope.() -> Unit
+ ) = lazyListState.scroll(scrollPriority, block)
+
+ override fun dispatchRawDelta(delta: Float): Float {
+ return lazyListState.dispatchRawDelta(delta)
+ }
+
+ override val isScrollInProgress: Boolean
+ get() = lazyListState.isScrollInProgress
+
+ override fun toString(): String = "PagerState(" +
+ "pageCount=$pageCount, " +
+ "currentPage=$currentPage, " +
+ "currentPageOffset=$currentPageOffset" +
+ ")"
+
+ private fun requireCurrentPage(value: Int, name: String) {
+ require(value >= 0) { "$name[$value] must be >= 0" }
+ }
+
+ private fun requireCurrentPageOffset(value: Float, name: String) {
+ require(value in -1f..1f) { "$name must be >= 0 and <= 1" }
+ }
+
+ companion object {
+ /**
+ * The default [Saver] implementation for [PagerState].
+ */
+ val Saver: Saver<PagerState, *> = listSaver(
+ save = {
+ listOf<Any>(
+ it.currentPage,
+ )
+ },
+ restore = {
+ PagerState(
+ currentPage = it[0] as Int,
+ )
+ }
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index e1ca69bd7940..965436847c64 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -31,4 +31,11 @@ object SettingsDimension {
end = itemPaddingEnd,
bottom = itemPaddingVertical,
)
+ val itemPaddingAround = 8.dp
+
+ /** The size when app icon is displayed in list. */
+ val appIconItemSize = 32.dp
+
+ /** The size when app icon is displayed in App info page. */
+ val appIconInfoSize = 48.dp
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt
new file mode 100644
index 000000000000..ba253368d505
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Collections.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.util
+
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.coroutineScope
+
+suspend inline fun <R, T> Iterable<T>.asyncMap(crossinline transform: (T) -> R): List<R> =
+ coroutineScope {
+ map { item ->
+ async { transform(item) }
+ }.awaitAll()
+ }
+
+suspend inline fun <T> Iterable<T>.asyncFilter(crossinline predicate: (T) -> Boolean): List<T> =
+ asyncMap { item -> item to predicate(item) }
+ .filter { it.second }
+ .map { it.first }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
new file mode 100644
index 000000000000..999d8d7dd16a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.util
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.snapshotFlow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+inline fun <T, R> Flow<List<T>>.asyncMapItem(crossinline transform: (T) -> R): Flow<List<R>> =
+ map { list -> list.asyncMap(transform) }
+
+@OptIn(ExperimentalCoroutinesApi::class)
+inline fun <T, R> Flow<T>.mapState(crossinline block: (T) -> State<R>): Flow<R> =
+ flatMapLatest { snapshotFlow { block(it).value } }
+
+fun <T1, T2> Flow<T1>.waitFirst(flow: Flow<T2>): Flow<T1> =
+ combine(flow.distinctUntilChangedBy {}) { value, _ -> value }
+
+class StateFlowBridge<T> {
+ private val stateFlow = MutableStateFlow<T?>(null)
+ val flow = stateFlow.filterNotNull()
+
+ fun setIfAbsent(value: T) {
+ if (stateFlow.value == null) {
+ stateFlow.value = value
+ }
+ }
+
+ @Composable
+ fun Sync(state: State<T>) {
+ LaunchedEffect(state.value) {
+ stateFlow.value = state.value
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
new file mode 100644
index 000000000000..0a41a1a95936
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.scaffold
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ArrowBack
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.android.settingslib.spa.framework.compose.LocalNavController
+
+@Composable
+internal fun NavigateUp() {
+ val navController = LocalNavController.current
+ val contentDescription = stringResource(
+ id = androidx.appcompat.R.string.abc_action_bar_up_description,
+ )
+ BackAction(contentDescription) {
+ navController.navigateUp()
+ }
+}
+
+@Composable
+private fun BackAction(contentDescription: String, onClick: () -> Unit) {
+ IconButton(onClick) {
+ Icon(
+ imageVector = Icons.Outlined.ArrowBack,
+ contentDescription = contentDescription,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt
new file mode 100644
index 000000000000..9a17b2a8cb78
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/RegularScaffold.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.scaffold
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+/**
+ * A [Scaffold] which content is scrollable and wrapped in a [Column].
+ *
+ * For example, this is for the pages with some preferences and is scrollable when the items out of
+ * the screen.
+ */
+@Composable
+fun RegularScaffold(
+ title: String,
+ actions: @Composable RowScope.() -> Unit = {},
+ content: @Composable () -> Unit,
+) {
+ SettingsScaffold(title, actions) { paddingValues ->
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ Spacer(Modifier.padding(paddingValues))
+ content()
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun RegularScaffoldPreview() {
+ SettingsTheme {
+ RegularScaffold(title = "Display") {}
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
index 1ec2390741ed..e0e9b951065b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
@@ -20,13 +20,14 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.TabRow
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import com.android.settingslib.spa.framework.compose.HorizontalPager
+import com.android.settingslib.spa.framework.compose.rememberPagerState
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import kotlin.math.absoluteValue
+import kotlinx.coroutines.launch
@Composable
fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit) {
@@ -37,10 +38,11 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit
}
Column {
- var currentPage by rememberSaveable { mutableStateOf(0) }
+ val coroutineScope = rememberCoroutineScope()
+ val pagerState = rememberPagerState()
TabRow(
- selectedTabIndex = currentPage,
+ selectedTabIndex = pagerState.currentPage,
modifier = Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd),
containerColor = Color.Transparent,
indicator = {},
@@ -49,12 +51,19 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit
titles.forEachIndexed { page, title ->
SettingsTab(
title = title,
- selected = currentPage == page,
- onClick = { currentPage = page },
+ selected = pagerState.currentPage == page,
+ currentPageOffset = pagerState.currentPageOffset.absoluteValue,
+ onClick = {
+ coroutineScope.launch {
+ pagerState.animateScrollToPage(page)
+ }
+ },
)
}
}
- content(currentPage)
+ HorizontalPager(count = titles.size, state = pagerState) { page ->
+ content(page)
+ }
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
new file mode 100644
index 000000000000..ee453f246623
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.scaffold
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SmallTopAppBar
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+/**
+ * A [Scaffold] which content is can be full screen when needed.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsScaffold(
+ title: String,
+ actions: @Composable RowScope.() -> Unit = {},
+ content: @Composable (PaddingValues) -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ SmallTopAppBar(
+ title = {
+ Text(
+ text = title,
+ modifier = Modifier.padding(SettingsDimension.itemPaddingAround),
+ )
+ },
+ navigationIcon = { NavigateUp() },
+ actions = actions,
+ colors = settingsTopAppBarColors(),
+ )
+ },
+ content = content,
+ )
+}
+
+@Composable
+internal fun settingsTopAppBarColors() = TopAppBarDefaults.largeTopAppBarColors(
+ containerColor = SettingsTheme.colorScheme.surfaceHeader,
+ scrolledContainerColor = SettingsTheme.colorScheme.surfaceHeader,
+)
+
+@Preview
+@Composable
+private fun SettingsScaffoldPreview() {
+ SettingsTheme {
+ SettingsScaffold(title = "Display") {}
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTab.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTab.kt
index 16d8dbc9bf74..30a4349e942f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTab.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTab.kt
@@ -26,6 +26,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsShape
@@ -35,8 +36,12 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme
internal fun SettingsTab(
title: String,
selected: Boolean,
+ currentPageOffset: Float,
onClick: () -> Unit,
) {
+ // Shows a color transition during pager scroll.
+ // 0f -> Selected, 1f -> Not selected
+ val colorFraction = if (selected) (currentPageOffset * 2).coerceAtMost(1f) else 1f
Tab(
selected = selected,
onClick = onClick,
@@ -44,29 +49,33 @@ internal fun SettingsTab(
.height(48.dp)
.padding(horizontal = 4.dp, vertical = 6.dp)
.clip(SettingsShape.CornerMedium)
- .background(color = when {
- selected -> SettingsTheme.colorScheme.primaryContainer
- else -> SettingsTheme.colorScheme.surface
- }),
+ .background(
+ color = lerp(
+ start = SettingsTheme.colorScheme.primaryContainer,
+ stop = SettingsTheme.colorScheme.surface,
+ fraction = colorFraction,
+ ),
+ ),
) {
Text(
text = title,
style = MaterialTheme.typography.labelLarge,
- color = when {
- selected -> SettingsTheme.colorScheme.onPrimaryContainer
- else -> SettingsTheme.colorScheme.secondaryText
- },
+ color = lerp(
+ start = SettingsTheme.colorScheme.onPrimaryContainer,
+ stop = SettingsTheme.colorScheme.secondaryText,
+ fraction = colorFraction,
+ ),
)
}
}
@Preview
@Composable
-private fun SettingsTabPreview() {
+fun SettingsTabPreview() {
SettingsTheme {
Column {
- SettingsTab(title = "Personal", selected = true) {}
- SettingsTab(title = "Work", selected = false) {}
+ SettingsTab(title = "Personal", selected = true, currentPageOffset = 0f) {}
+ SettingsTab(title = "Work", selected = false, currentPageOffset = 0f) {}
}
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index a414c89dc24c..59b413cef56e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -16,10 +16,14 @@
package com.android.settingslib.spa.widget.ui
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
@Composable
fun SettingsTitle(title: State<String>) {
@@ -50,3 +54,17 @@ fun SettingsBody(body: String) {
)
}
}
+
+@Composable
+fun PlaceholderTitle(title: String) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleLarge,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
index f608e1003163..0c84eac45cb7 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsPagerKtTest.kt
@@ -18,6 +18,7 @@ package com.android.settingslib.spa.widget.scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotSelected
import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.junit4.createComposeRule
@@ -43,7 +44,7 @@ class SettingsPagerKtTest {
composeTestRule.onNodeWithText("Personal").assertIsSelected()
composeTestRule.onNodeWithText("Page 0").assertIsDisplayed()
composeTestRule.onNodeWithText("Work").assertIsNotSelected()
- composeTestRule.onNodeWithText("Page 1").assertDoesNotExist()
+ composeTestRule.onNodeWithText("Page 1").assertIsNotDisplayed()
}
@Test
@@ -55,7 +56,7 @@ class SettingsPagerKtTest {
composeTestRule.onNodeWithText("Work").performClick()
composeTestRule.onNodeWithText("Personal").assertIsNotSelected()
- composeTestRule.onNodeWithText("Page 0").assertDoesNotExist()
+ composeTestRule.onNodeWithText("Page 0").assertIsNotDisplayed()
composeTestRule.onNodeWithText("Work").assertIsSelected()
composeTestRule.onNodeWithText("Page 1").assertIsDisplayed()
}
diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp
index ecbb219c64b5..a6469b5172bb 100644
--- a/packages/SettingsLib/SpaPrivileged/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/Android.bp
@@ -28,5 +28,8 @@ android_library {
"SettingsLib",
"androidx.compose.runtime_runtime",
],
- kotlincflags: ["-Xjvm-default=all"],
+ kotlincflags: [
+ "-Xjvm-default=all",
+ "-Xopt-in=kotlin.RequiresOptIn",
+ ],
}
diff --git a/packages/SettingsLib/SpaPrivileged/res/values/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
new file mode 100644
index 000000000000..8f8dd2b01ecd
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- [CHAR LIMIT=25] Text shown when there are no applications to display. -->
+ <string name="no_applications">No apps.</string>
+</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
new file mode 100644
index 000000000000..2fa869c529d4
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListModel.kt
@@ -0,0 +1,26 @@
+package com.android.settingslib.spaprivileged.model.app
+
+import android.content.pm.ApplicationInfo
+import android.icu.text.CollationKey
+import kotlinx.coroutines.flow.Flow
+
+data class AppEntry<T : AppRecord>(
+ val record: T,
+ val label: String,
+ val labelCollationKey: CollationKey,
+)
+
+interface AppListModel<T : AppRecord> {
+ fun getSpinnerOptions(): List<String> = emptyList()
+ fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>): Flow<List<T>>
+ fun filter(userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<T>>): Flow<List<T>>
+
+ suspend fun onFirstLoaded(recordList: List<T>) {}
+ fun getComparator(option: Int): Comparator<AppEntry<T>> = compareBy(
+ { it.labelCollationKey },
+ { it.record.app.packageName },
+ { it.record.app.uid },
+ )
+
+ fun getSummary(option: Int, record: T): Flow<String>?
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
new file mode 100644
index 000000000000..9265158b3b4a
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.app.Application
+import android.content.pm.ApplicationInfo
+import android.content.pm.UserInfo
+import android.icu.text.Collator
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.settingslib.spa.framework.util.StateFlowBridge
+import com.android.settingslib.spa.framework.util.asyncMapItem
+import com.android.settingslib.spa.framework.util.waitFirst
+import java.util.concurrent.ConcurrentHashMap
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.plus
+
+internal data class AppListData<T : AppRecord>(
+ val appEntries: List<AppEntry<T>>,
+ val option: Int,
+) {
+ fun filter(predicate: (AppEntry<T>) -> Boolean) =
+ AppListData(appEntries.filter(predicate), option)
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+internal class AppListViewModel<T : AppRecord>(
+ application: Application,
+) : AndroidViewModel(application) {
+ val userInfo = StateFlowBridge<UserInfo>()
+ val listModel = StateFlowBridge<AppListModel<T>>()
+ val showSystem = StateFlowBridge<Boolean>()
+ val option = StateFlowBridge<Int>()
+ val searchQuery = StateFlowBridge<String>()
+
+ private val appsRepository = AppsRepository(application)
+ private val appRepository = AppRepositoryImpl(application)
+ private val collator = Collator.getInstance().freeze()
+ private val labelMap = ConcurrentHashMap<String, String>()
+ private val scope = viewModelScope + Dispatchers.Default
+
+ private val userIdFlow = userInfo.flow.map { it.id }
+
+ private val recordListFlow = listModel.flow
+ .flatMapLatest { it.transform(userIdFlow, appsRepository.loadApps(userInfo.flow)) }
+ .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+ private val systemFilteredFlow = appsRepository.showSystemPredicate(userIdFlow, showSystem.flow)
+ .combine(recordListFlow) { showAppPredicate, recordList ->
+ recordList.filter { showAppPredicate(it.app) }
+ }
+
+ val appListDataFlow = option.flow.flatMapLatest(::filterAndSort)
+ .combine(searchQuery.flow) { appListData, searchQuery ->
+ appListData.filter {
+ it.label.contains(other = searchQuery, ignoreCase = true)
+ }
+ }
+ .shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
+
+ init {
+ scheduleOnFirstLoaded()
+ }
+
+ private fun filterAndSort(option: Int) = listModel.flow.flatMapLatest { listModel ->
+ listModel.filter(userIdFlow, option, systemFilteredFlow)
+ .asyncMapItem { record ->
+ val label = getLabel(record.app)
+ AppEntry(
+ record = record,
+ label = label,
+ labelCollationKey = collator.getCollationKey(label),
+ )
+ }
+ .map { appEntries ->
+ AppListData(
+ appEntries = appEntries.sortedWith(listModel.getComparator(option)),
+ option = option,
+ )
+ }
+ }
+
+ private fun scheduleOnFirstLoaded() {
+ recordListFlow
+ .waitFirst(appListDataFlow)
+ .combine(listModel.flow) { recordList, listModel ->
+ listModel.maybePreFetchLabels(recordList)
+ listModel.onFirstLoaded(recordList)
+ }
+ .launchIn(scope)
+ }
+
+ private fun AppListModel<T>.maybePreFetchLabels(recordList: List<T>) {
+ if (getSpinnerOptions().isNotEmpty()) {
+ for (record in recordList) {
+ getLabel(record.app)
+ }
+ }
+ }
+
+ private fun getLabel(app: ApplicationInfo) = labelMap.computeIfAbsent(app.packageName) {
+ appRepository.loadLabel(app)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppOpsController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
index 3808f64386ef..c2d85a54bc32 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppOpsController.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.framework.app
+package com.android.settingslib.spaprivileged.model.app
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRecord.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRecord.kt
index 8dde897cb23d..297868871389 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRecord.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRecord.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.framework.app
+package com.android.settingslib.spaprivileged.model.app
import android.content.pm.ApplicationInfo
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
index a6378ef53437..34f12af28dce 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppRepository.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.framework.app
+package com.android.settingslib.spaprivileged.model.app
import android.content.Context
import android.content.pm.ApplicationInfo
@@ -31,6 +31,8 @@ import kotlinx.coroutines.withContext
fun rememberAppRepository(): AppRepository = rememberContext(::AppRepositoryImpl)
interface AppRepository {
+ fun loadLabel(app: ApplicationInfo): String
+
@Composable
fun produceLabel(app: ApplicationInfo): State<String>
@@ -38,9 +40,11 @@ interface AppRepository {
fun produceIcon(app: ApplicationInfo): State<Drawable?>
}
-private class AppRepositoryImpl(private val context: Context) : AppRepository {
+internal class AppRepositoryImpl(private val context: Context) : AppRepository {
private val packageManager = context.packageManager
+ override fun loadLabel(app: ApplicationInfo): String = app.loadLabel(packageManager).toString()
+
@Composable
override fun produceLabel(app: ApplicationInfo) = produceState(initialValue = "", app) {
withContext(Dispatchers.Default) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/Apps.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt
index f6755459c1b7..6e1afd94fe29 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/Apps.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.framework.app
+package com.android.settingslib.spaprivileged.model.app
import android.content.pm.ApplicationInfo
import android.os.UserHandle
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
new file mode 100644
index 000000000000..6a6462098230
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.UserInfo
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+class AppsRepository(context: Context) {
+ private val packageManager = context.packageManager
+
+ fun loadApps(userInfoFlow: Flow<UserInfo>): Flow<List<ApplicationInfo>> = userInfoFlow
+ .map { loadApps(it) }
+ .flowOn(Dispatchers.Default)
+
+ private suspend fun loadApps(userInfo: UserInfo): List<ApplicationInfo> {
+ return coroutineScope {
+ val hiddenSystemModulesDeferred = async {
+ packageManager.getInstalledModules(0)
+ .filter { it.isHidden }
+ .map { it.packageName }
+ .toSet()
+ }
+ val flags = PackageManager.ApplicationInfoFlags.of(
+ ((if (userInfo.isAdmin) PackageManager.MATCH_ANY_USER else 0) or
+ PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
+ )
+ val installedApplicationsAsUser =
+ packageManager.getInstalledApplicationsAsUser(flags, userInfo.id)
+
+ val hiddenSystemModules = hiddenSystemModulesDeferred.await()
+ installedApplicationsAsUser.filter { app ->
+ app.isInAppList(hiddenSystemModules)
+ }
+ }
+ }
+
+ fun showSystemPredicate(
+ userIdFlow: Flow<Int>,
+ showSystemFlow: Flow<Boolean>,
+ ): Flow<(app: ApplicationInfo) -> Boolean> =
+ userIdFlow.combine(showSystemFlow) { userId, showSystem ->
+ showSystemPredicate(userId, showSystem)
+ }
+
+ private suspend fun showSystemPredicate(
+ userId: Int,
+ showSystem: Boolean,
+ ): (app: ApplicationInfo) -> Boolean {
+ if (showSystem) return { true }
+ val homeOrLauncherPackages = loadHomeOrLauncherPackages(userId)
+ return { app ->
+ app.isUpdatedSystemApp || !app.isSystemApp || app.packageName in homeOrLauncherPackages
+ }
+ }
+
+ private suspend fun loadHomeOrLauncherPackages(userId: Int): Set<String> {
+ val launchIntent = Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)
+ // If we do not specify MATCH_DIRECT_BOOT_AWARE or MATCH_DIRECT_BOOT_UNAWARE, system will
+ // derive and update the flags according to the user's lock state. When the user is locked,
+ // components with ComponentInfo#directBootAware == false will be filtered. We should
+ // explicitly include both direct boot aware and unaware component here.
+ val flags = PackageManager.ResolveInfoFlags.of(
+ (PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE).toLong()
+ )
+ return coroutineScope {
+ val launcherActivities = async {
+ packageManager.queryIntentActivitiesAsUser(launchIntent, flags, userId)
+ }
+ val homeActivities = ArrayList<ResolveInfo>()
+ packageManager.getHomeActivities(homeActivities)
+ (launcherActivities.await() + homeActivities)
+ .map { it.activityInfo.packageName }
+ .toSet()
+ }
+ }
+
+ companion object {
+ private fun ApplicationInfo.isInAppList(hiddenSystemModules: Set<String>) =
+ when {
+ packageName in hiddenSystemModules -> false
+ enabled -> true
+ enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true
+ else -> false
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
index 66b05da03b3f..0cc497a3e899 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.framework.app
+package com.android.settingslib.spaprivileged.model.app
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index ae0cb772533c..fab3ae8e510b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.framework.enterprise
+package com.android.settingslib.spaprivileged.model.enterprise
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index 5ae514cfb524..58d0f8d398f2 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -29,12 +29,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
+import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SettingsTitle
-import com.android.settingslib.spaprivileged.framework.app.PackageManagers
-import com.android.settingslib.spaprivileged.framework.app.rememberAppRepository
+import com.android.settingslib.spaprivileged.model.app.PackageManagers
+import com.android.settingslib.spaprivileged.model.app.rememberAppRepository
@Composable
fun AppInfo(packageName: String, userId: Int) {
@@ -45,7 +47,7 @@ fun AppInfo(packageName: String, userId: Int) {
horizontalAlignment = Alignment.CenterHorizontally) {
val packageInfo = remember { PackageManagers.getPackageInfoAsUser(packageName, userId) }
Box(modifier = Modifier.padding(8.dp)) {
- AppIcon(app = packageInfo.applicationInfo, size = 48)
+ AppIcon(app = packageInfo.applicationInfo, size = SettingsDimension.appIconInfoSize)
}
AppLabel(packageInfo.applicationInfo)
Spacer(modifier = Modifier.height(4.dp))
@@ -54,12 +56,12 @@ fun AppInfo(packageName: String, userId: Int) {
}
@Composable
-fun AppIcon(app: ApplicationInfo, size: Int) {
+fun AppIcon(app: ApplicationInfo, size: Dp) {
val appRepository = rememberAppRepository()
Image(
painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
contentDescription = null,
- modifier = Modifier.size(size.dp)
+ modifier = Modifier.size(size)
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
new file mode 100644
index 000000000000..c60976ddea4d
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import android.content.pm.UserInfo
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.settingslib.spa.framework.compose.LogCompositions
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.ui.PlaceholderTitle
+import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListData
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppListViewModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import kotlinx.coroutines.Dispatchers
+
+private const val TAG = "AppList"
+
+@Composable
+fun <T : AppRecord> AppList(
+ userInfo: UserInfo,
+ listModel: AppListModel<T>,
+ showSystem: State<Boolean>,
+ option: State<Int>,
+ searchQuery: State<String>,
+ appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
+) {
+ LogCompositions(TAG, userInfo.id.toString())
+ val appListData = loadAppEntries(userInfo, listModel, showSystem, option, searchQuery)
+ AppListWidget(appListData, listModel, appItem)
+}
+
+@Composable
+private fun <T : AppRecord> AppListWidget(
+ appListData: State<AppListData<T>?>,
+ listModel: AppListModel<T>,
+ appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
+) {
+ appListData.value?.let { (list, option) ->
+ if (list.isEmpty()) {
+ PlaceholderTitle(stringResource(R.string.no_applications))
+ return
+ }
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ state = rememberLazyListState(),
+ contentPadding = PaddingValues(bottom = SettingsDimension.itemPaddingVertical),
+ ) {
+ items(count = list.size, key = { option to list[it].record.app.packageName }) {
+ val appEntry = list[it]
+ val summary = getSummary(listModel, option, appEntry.record)
+ val itemModel = remember(appEntry) {
+ AppListItemModel(appEntry.record, appEntry.label, summary)
+ }
+ appItem(itemModel)
+ }
+ }
+ }
+}
+
+@Composable
+private fun <T : AppRecord> loadAppEntries(
+ userInfo: UserInfo,
+ listModel: AppListModel<T>,
+ showSystem: State<Boolean>,
+ option: State<Int>,
+ searchQuery: State<String>,
+): State<AppListData<T>?> {
+ val viewModel: AppListViewModel<T> = viewModel(key = userInfo.id.toString())
+ viewModel.userInfo.setIfAbsent(userInfo)
+ viewModel.listModel.setIfAbsent(listModel)
+ viewModel.showSystem.Sync(showSystem)
+ viewModel.option.Sync(option)
+ viewModel.searchQuery.Sync(searchQuery)
+
+ return viewModel.appListDataFlow.collectAsState(null, Dispatchers.Default)
+}
+
+@Composable
+private fun <T : AppRecord> getSummary(
+ listModel: AppListModel<T>,
+ option: Int,
+ record: T,
+): State<String> = remember(option) { listModel.getSummary(option, record) }
+ ?.collectAsState(stringResource(R.string.summary_placeholder), Dispatchers.Default)
+ ?: "".toState()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt
new file mode 100644
index 000000000000..ac3f8ff79091
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListItem.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+
+class AppListItemModel<T : AppRecord>(
+ val record: T,
+ val label: String,
+ val summary: State<String>,
+)
+
+@Composable
+fun <T : AppRecord> AppListItem(
+ itemModel: AppListItemModel<T>,
+ onClick: () -> Unit,
+) {
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = itemModel.label
+ override val summary = itemModel.summary
+ override val icon = @Composable {
+ AppIcon(app = itemModel.record.app, size = SettingsDimension.appIconItemSize)
+ }
+ override val onClick = onClick
+ }
+ })
+}
+
+@Preview
+@Composable
+private fun AppListItemPreview() {
+ SettingsTheme {
+ val record = object : AppRecord {
+ override val app = LocalContext.current.applicationInfo
+ }
+ val itemModel = AppListItemModel<AppRecord>(record, "Chrome", "Allowed".toState())
+ AppListItem(itemModel) {}
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 57e9e9ac1b9b..430ac5409711 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -31,8 +31,8 @@ import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spaprivileged.framework.app.AppRecord
-import com.android.settingslib.spaprivileged.framework.app.PackageManagers
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.PackageManagers
import kotlinx.coroutines.Dispatchers
private const val PERMISSION = "permission"
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
index 88ad9daa85b3..3782f7cd301e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListModel.kt
@@ -20,7 +20,7 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import com.android.settingslib.spaprivileged.framework.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.AppRecord
interface TogglePermissionAppListModel<T : AppRecord> {
val pageTitleResId: Int
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/WorkProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
index 09864a17adfc..aa5ccf146d47 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/WorkProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/WorkProfilePager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.template.scaffold
+package com.android.settingslib.spaprivileged.template.common
import android.content.pm.UserInfo
import android.os.UserHandle
@@ -23,7 +23,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import com.android.settingslib.spa.widget.scaffold.SettingsPager
-import com.android.settingslib.spaprivileged.framework.enterprise.EnterpriseRepository
+import com.android.settingslib.spaprivileged.model.enterprise.EnterpriseRepository
@Composable
fun WorkProfilePager(content: @Composable (userInfo: UserInfo) -> Unit) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index aab0d3a08154..06d7bb49c809 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1601,21 +1601,6 @@
<!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_no_calling">No calling.</string>
- <!-- Screensaver overlay which displays the time. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_time">Time</string>
- <!-- Screensaver overlay which displays the date. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_date">Date</string>
- <!-- Screensaver overlay which displays the weather. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_weather">Weather</string>
- <!-- Screensaver overlay which displays air quality. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_aqi">Air Quality</string>
- <!-- Screensaver overlay which displays cast info. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_cast_info">Cast Info</string>
- <!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_home_controls">Home Controls</string>
- <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
- <string name="dream_complication_title_smartspace">Smartspace</string>
-
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
<string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index b9c4030d9d0e..a822e185479a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -600,6 +600,9 @@ public class Utils {
* Returns the WifiInfo for the underlying WiFi network of the VCN network, returns null if the
* input NetworkCapabilities is not for a VCN network with underlying WiFi network.
*
+ * TODO(b/238425913): Move this method to be inside systemui not settingslib once we've migrated
+ * off of {@link WifiStatusTracker} and {@link NetworkControllerImpl}.
+ *
* @param networkCapabilities NetworkCapabilities of the network.
*/
@Nullable
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 22586171e5cf..1606540da3fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -17,7 +17,6 @@
package com.android.settingslib.dream;
import android.annotation.IntDef;
-import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,17 +31,14 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
-import android.text.TextUtils;
import android.util.Log;
-import com.android.settingslib.R;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -64,18 +60,21 @@ public class DreamBackend {
public String toString() {
StringBuilder sb = new StringBuilder(DreamInfo.class.getSimpleName());
sb.append('[').append(caption);
- if (isActive)
+ if (isActive) {
sb.append(",active");
+ }
sb.append(',').append(componentName);
- if (settingsComponentName != null)
+ if (settingsComponentName != null) {
sb.append("settings=").append(settingsComponentName);
+ }
return sb.append(']').toString();
}
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({WHILE_CHARGING, WHILE_DOCKED, EITHER, NEVER})
- public @interface WhenToDream {}
+ public @interface WhenToDream {
+ }
public static final int WHILE_CHARGING = 0;
public static final int WHILE_DOCKED = 1;
@@ -96,7 +95,8 @@ public class DreamBackend {
COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
- public @interface ComplicationType {}
+ public @interface ComplicationType {
+ }
public static final int COMPLICATION_TYPE_TIME = 1;
public static final int COMPLICATION_TYPE_DATE = 2;
@@ -114,8 +114,6 @@ public class DreamBackend {
private final boolean mDreamsActivatedOnDockByDefault;
private final Set<ComponentName> mDisabledDreams;
private final Set<Integer> mSupportedComplications;
- private final Set<Integer> mDefaultEnabledComplications;
-
private static DreamBackend sInstance;
public static DreamBackend getInstance(Context context) {
@@ -147,13 +145,6 @@ public class DreamBackend {
com.android.internal.R.array.config_supportedDreamComplications))
.boxed()
.collect(Collectors.toSet());
-
- mDefaultEnabledComplications = Arrays.stream(resources.getIntArray(
- com.android.internal.R.array.config_dreamComplicationsEnabledByDefault))
- .boxed()
- // A complication can only be enabled by default if it is also supported.
- .filter(mSupportedComplications::contains)
- .collect(Collectors.toSet());
}
public List<DreamInfo> getDreamInfos() {
@@ -251,11 +242,12 @@ public class DreamBackend {
return null;
}
- public @WhenToDream int getWhenToDreamSetting() {
+ @WhenToDream
+ public int getWhenToDreamSetting() {
return isActivatedOnDock() && isActivatedOnSleep() ? EITHER
: isActivatedOnDock() ? WHILE_DOCKED
- : isActivatedOnSleep() ? WHILE_CHARGING
- : NEVER;
+ : isActivatedOnSleep() ? WHILE_CHARGING
+ : NEVER;
}
public void setWhenToDream(@WhenToDream int whenToDream) {
@@ -283,98 +275,29 @@ public class DreamBackend {
}
}
- /** Returns whether a particular complication is enabled */
- public boolean isComplicationEnabled(@ComplicationType int complication) {
- return getEnabledComplications().contains(complication);
- }
-
/** Gets all complications which have been enabled by the user. */
public Set<Integer> getEnabledComplications() {
- final String enabledComplications = Settings.Secure.getString(
- mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS);
-
- if (enabledComplications == null) {
- return mDefaultEnabledComplications;
- }
-
- return parseFromString(enabledComplications);
+ return getComplicationsEnabled() ? mSupportedComplications : Collections.emptySet();
}
- /** Gets all dream complications which are supported on this device. **/
- public Set<Integer> getSupportedComplications() {
- return mSupportedComplications;
- }
-
- /**
- * Enables or disables a particular dream complication.
- *
- * @param complicationType The dream complication to be enabled/disabled.
- * @param value If true, the complication is enabled. Otherwise it is disabled.
- */
- public void setComplicationEnabled(@ComplicationType int complicationType, boolean value) {
- if (!mSupportedComplications.contains(complicationType)) return;
-
- Set<Integer> enabledComplications = getEnabledComplications();
- if (value) {
- enabledComplications.add(complicationType);
- } else {
- enabledComplications.remove(complicationType);
- }
-
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
- convertToString(enabledComplications));
+ /** Sets complication enabled state. */
+ public void setComplicationsEnabled(boolean enabled) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, enabled ? 1 : 0);
}
/**
- * Gets the title of a particular complication type to be displayed to the user. If there
- * is no title, null is returned.
+ * Gets whether complications are enabled on this device
*/
- @Nullable
- public CharSequence getComplicationTitle(@ComplicationType int complicationType) {
- int res = 0;
- switch (complicationType) {
- case COMPLICATION_TYPE_TIME:
- res = R.string.dream_complication_title_time;
- break;
- case COMPLICATION_TYPE_DATE:
- res = R.string.dream_complication_title_date;
- break;
- case COMPLICATION_TYPE_WEATHER:
- res = R.string.dream_complication_title_weather;
- break;
- case COMPLICATION_TYPE_AIR_QUALITY:
- res = R.string.dream_complication_title_aqi;
- break;
- case COMPLICATION_TYPE_CAST_INFO:
- res = R.string.dream_complication_title_cast_info;
- break;
- case COMPLICATION_TYPE_HOME_CONTROLS:
- res = R.string.dream_complication_title_home_controls;
- break;
- case COMPLICATION_TYPE_SMARTSPACE:
- res = R.string.dream_complication_title_smartspace;
- break;
- default:
- return null;
- }
- return mContext.getString(res);
- }
-
- private static String convertToString(Set<Integer> set) {
- return set.stream()
- .map(String::valueOf)
- .collect(Collectors.joining(","));
+ public boolean getComplicationsEnabled() {
+ return Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, 1) == 1;
}
- private static Set<Integer> parseFromString(String string) {
- if (TextUtils.isEmpty(string)) {
- return new HashSet<>();
- }
- return Arrays.stream(string.split(","))
- .map(Integer::parseInt)
- .collect(Collectors.toSet());
+ /** Gets all dream complications which are supported on this device. **/
+ public Set<Integer> getSupportedComplications() {
+ return mSupportedComplications;
}
public boolean isEnabled() {
@@ -416,10 +339,11 @@ public class DreamBackend {
public void setActiveDream(ComponentName dream) {
logd("setActiveDream(%s)", dream);
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return;
+ }
try {
- ComponentName[] dreams = { dream };
+ ComponentName[] dreams = {dream};
mDreamManager.setDreamComponents(dream == null ? null : dreams);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set active dream to " + dream, e);
@@ -427,8 +351,9 @@ public class DreamBackend {
}
public ComponentName getActiveDream() {
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return null;
+ }
try {
ComponentName[] dreams = mDreamManager.getDreamComponents();
return dreams != null && dreams.length > 0 ? dreams[0] : null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/OWNERS b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
new file mode 100644
index 000000000000..d40f322042fe
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
@@ -0,0 +1,2 @@
+# Default reviewers for this and subdirectories.
+shaoweishen@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 86f7850cf1f2..52b9227fb373 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -34,29 +35,35 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowSettings;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSettings.ShadowSecure.class})
public final class DreamBackendTest {
private static final int[] SUPPORTED_DREAM_COMPLICATIONS = {1, 2, 3};
- private static final int[] DEFAULT_DREAM_COMPLICATIONS = {1, 3, 4};
+ private static final List<Integer> SUPPORTED_DREAM_COMPLICATIONS_LIST = Arrays.stream(
+ SUPPORTED_DREAM_COMPLICATIONS).boxed().collect(
+ Collectors.toList());
@Mock
private Context mContext;
+ @Mock
+ private ContentResolver mMockResolver;
private DreamBackend mBackend;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
final Resources res = mock(Resources.class);
when(mContext.getResources()).thenReturn(res);
when(res.getIntArray(
com.android.internal.R.array.config_supportedDreamComplications)).thenReturn(
SUPPORTED_DREAM_COMPLICATIONS);
- when(res.getIntArray(
- com.android.internal.R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
- DEFAULT_DREAM_COMPLICATIONS);
when(res.getStringArray(
com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
new String[]{});
@@ -69,31 +76,25 @@ public final class DreamBackendTest {
}
@Test
- public void testSupportedComplications() {
- assertThat(mBackend.getSupportedComplications()).containsExactly(1, 2, 3);
- }
-
- @Test
- public void testGetEnabledDreamComplications_default() {
- assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
- }
-
- @Test
- public void testEnableComplication() {
- mBackend.setComplicationEnabled(/* complicationType= */ 2, true);
- assertThat(mBackend.getEnabledComplications()).containsExactly(1, 2, 3);
+ public void testComplicationsEnabledByDefault() {
+ assertThat(mBackend.getComplicationsEnabled()).isTrue();
+ assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
+ SUPPORTED_DREAM_COMPLICATIONS_LIST);
}
@Test
- public void testEnableComplication_notSupported() {
- mBackend.setComplicationEnabled(/* complicationType= */ 5, true);
- assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+ public void testEnableComplicationExplicitly() {
+ mBackend.setComplicationsEnabled(true);
+ assertThat(mBackend.getEnabledComplications()).containsExactlyElementsIn(
+ SUPPORTED_DREAM_COMPLICATIONS_LIST);
+ assertThat(mBackend.getComplicationsEnabled()).isTrue();
}
@Test
- public void testDisableComplication() {
- mBackend.setComplicationEnabled(/* complicationType= */ 1, false);
- assertThat(mBackend.getEnabledComplications()).containsExactly(3);
+ public void testDisableComplications() {
+ mBackend.setComplicationsEnabled(false);
+ assertThat(mBackend.getEnabledComplications()).isEmpty();
+ assertThat(mBackend.getComplicationsEnabled()).isFalse();
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 652e281e67e9..78dea891bc12 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -85,6 +85,7 @@
<uses-permission android:name="android.permission.CONTROL_VPN" />
<uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL"/>
+ <uses-permission android:name="android.permission.NETWORK_STACK"/>
<!-- Physical hardware -->
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
diff --git a/packages/SystemUI/res-keyguard/values-land/dimens.xml b/packages/SystemUI/res-keyguard/values-land/dimens.xml
index 4e92884f39f3..a4e7a5f12db4 100644
--- a/packages/SystemUI/res-keyguard/values-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-land/dimens.xml
@@ -22,7 +22,6 @@
<dimen name="keyguard_eca_top_margin">0dp</dimen>
<dimen name="keyguard_eca_bottom_margin">2dp</dimen>
<dimen name="keyguard_password_height">26dp</dimen>
- <dimen name="num_pad_entry_row_margin_bottom">0dp</dimen>
<!-- The size of PIN text in the PIN unlock method. -->
<integer name="scaled_password_text_size">26</integer>
diff --git a/packages/SystemUI/res-keyguard/values-sw360dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw360dp-land/dimens.xml
index f465be4f5228..0421135b31a5 100644
--- a/packages/SystemUI/res-keyguard/values-sw360dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw360dp-land/dimens.xml
@@ -22,7 +22,6 @@
<dimen name="keyguard_eca_top_margin">4dp</dimen>
<dimen name="keyguard_eca_bottom_margin">4dp</dimen>
<dimen name="keyguard_password_height">50dp</dimen>
- <dimen name="num_pad_entry_row_margin_bottom">4dp</dimen>
<!-- The size of PIN text in the PIN unlock method. -->
<integer name="scaled_password_text_size">40</integer>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index acf3e4dcf02a..32871f0abb4f 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -86,7 +86,7 @@
<!-- Spacing around each button used for PIN view -->
<dimen name="num_pad_key_width">72dp</dimen>
- <dimen name="num_pad_entry_row_margin_bottom">16dp</dimen>
+ <dimen name="num_pad_entry_row_margin_bottom">12dp</dimen>
<dimen name="num_pad_row_margin_bottom">6dp</dimen>
<dimen name="num_pad_key_margin_end">12dp</dimen>
diff --git a/packages/SystemUI/res/layout/new_status_bar_wifi_group.xml b/packages/SystemUI/res/layout/new_status_bar_wifi_group.xml
new file mode 100644
index 000000000000..753ba2f21faf
--- /dev/null
+++ b/packages/SystemUI/res/layout/new_status_bar_wifi_group.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/wifi_combo"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical" >
+
+ <include layout="@layout/status_bar_wifi_group_inner" />
+
+</com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView>
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml
index 35cce25d45a7..6cb6993bb762 100644
--- a/packages/SystemUI/res/layout/status_bar_wifi_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group.xml
@@ -18,70 +18,11 @@
-->
<com.android.systemui.statusbar.StatusBarWifiView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/wifi_combo"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical" >
- <com.android.keyguard.AlphaOptimizedLinearLayout
- android:id="@+id/wifi_group"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:layout_marginStart="2.5dp"
- >
- <FrameLayout
- android:id="@+id/inout_container"
- android:layout_height="17dp"
- android:layout_width="wrap_content"
- android:gravity="center_vertical" >
- <ImageView
- android:id="@+id/wifi_in"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/ic_activity_down"
- android:visibility="gone"
- android:paddingEnd="2dp"
- />
- <ImageView
- android:id="@+id/wifi_out"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/ic_activity_up"
- android:paddingEnd="2dp"
- android:visibility="gone"
- />
- </FrameLayout>
- <FrameLayout
- android:id="@+id/wifi_combo"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="center_vertical" >
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/wifi_signal"
- android:layout_height="@dimen/status_bar_wifi_signal_size"
- android:layout_width="@dimen/status_bar_wifi_signal_size" />
- </FrameLayout>
+ <include layout="@layout/status_bar_wifi_group_inner" />
- <View
- android:id="@+id/wifi_signal_spacer"
- android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
- android:layout_height="4dp"
- android:visibility="gone" />
-
- <!-- Looks like CarStatusBar uses this... -->
- <ViewStub
- android:id="@+id/connected_device_signals_stub"
- android:layout="@layout/connected_device_signal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <View
- android:id="@+id/wifi_airplane_spacer"
- android:layout_width="@dimen/status_bar_airplane_spacer_width"
- android:layout_height="4dp"
- android:visibility="gone"
- />
- </com.android.keyguard.AlphaOptimizedLinearLayout>
-</com.android.systemui.statusbar.StatusBarWifiView>
+</com.android.systemui.statusbar.StatusBarWifiView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
new file mode 100644
index 000000000000..0ea0653ab89f
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <com.android.keyguard.AlphaOptimizedLinearLayout
+ android:id="@+id/wifi_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:layout_marginStart="2.5dp"
+ >
+ <FrameLayout
+ android:id="@+id/inout_container"
+ android:layout_height="17dp"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical" >
+ <ImageView
+ android:id="@+id/wifi_in"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/ic_activity_down"
+ android:visibility="gone"
+ android:paddingEnd="2dp"
+ />
+ <ImageView
+ android:id="@+id/wifi_out"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/ic_activity_up"
+ android:paddingEnd="2dp"
+ android:visibility="gone"
+ />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/wifi_combo"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical" >
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/wifi_signal"
+ android:layout_height="@dimen/status_bar_wifi_signal_size"
+ android:layout_width="@dimen/status_bar_wifi_signal_size" />
+ </FrameLayout>
+
+ <View
+ android:id="@+id/wifi_signal_spacer"
+ android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
+ android:layout_height="4dp"
+ android:visibility="gone" />
+
+ <!-- Looks like CarStatusBar uses this... -->
+ <ViewStub
+ android:id="@+id/connected_device_signals_stub"
+ android:layout="@layout/connected_device_signal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <View
+ android:id="@+id/wifi_airplane_spacer"
+ android:layout_width="@dimen/status_bar_airplane_spacer_width"
+ android:layout_height="4dp"
+ android:visibility="gone"
+ />
+ </com.android.keyguard.AlphaOptimizedLinearLayout>
+</merge>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7c1fdd548066..ae30089358fe 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1179,7 +1179,6 @@
<item name="shutdown_scrim_behind_alpha" format="float" type="dimen">0.95</item>
<!-- Output switcher panel related dimensions -->
- <dimen name="media_output_dialog_list_margin">12dp</dimen>
<dimen name="media_output_dialog_list_max_height">355dp</dimen>
<dimen name="media_output_dialog_header_album_icon_size">72dp</dimen>
<dimen name="media_output_dialog_header_back_icon_size">32dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 8f1959e884cf..a21a78b92d84 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -50,7 +50,12 @@ class AnimatableClockView @JvmOverloads constructor(
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
- private var lastMeasureCall: CharSequence = ""
+ private var lastMeasureCall: CharSequence? = null
+ private var lastDraw: CharSequence? = null
+ private var lastTextUpdate: CharSequence? = null
+ private var lastOnTextChanged: CharSequence? = null
+ private var lastInvalidate: CharSequence? = null
+ private var lastTimeZoneChange: CharSequence? = null
private val time = Calendar.getInstance()
@@ -142,7 +147,6 @@ class AnimatableClockView @JvmOverloads constructor(
// relayout if the text didn't actually change.
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
-
// Because the TextLayout may mutate under the hood as a result of the new text, we
// notify the TextAnimator that it may have changed and request a measure/layout. A
// crash will occur on the next invocation of setTextStyle if the layout is mutated
@@ -151,18 +155,19 @@ class AnimatableClockView @JvmOverloads constructor(
textAnimator?.updateLayout(layout)
}
requestLayout()
+ lastTextUpdate = getTimestamp()
}
}
fun onTimeZoneChanged(timeZone: TimeZone?) {
time.timeZone = timeZone
refreshFormat()
+ lastTimeZoneChange = "${getTimestamp()} timeZone=${time.timeZone}"
}
@SuppressLint("DrawAllocation")
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- lastMeasureCall = DateFormat.format(descFormat, System.currentTimeMillis())
val animator = textAnimator
if (animator == null) {
textAnimator = TextAnimator(layout) { invalidate() }
@@ -171,13 +176,34 @@ class AnimatableClockView @JvmOverloads constructor(
} else {
animator.updateLayout(layout)
}
+ lastMeasureCall = getTimestamp()
}
override fun onDraw(canvas: Canvas) {
+ lastDraw = getTimestamp()
// intentionally doesn't call super.onDraw here or else the text will be rendered twice
textAnimator?.draw(canvas)
}
+ override fun invalidate() {
+ super.invalidate()
+ lastInvalidate = getTimestamp()
+ }
+
+ private fun getTimestamp(): CharSequence {
+ return "${DateFormat.format("HH:mm:ss", System.currentTimeMillis())} text=$text"
+ }
+
+ override fun onTextChanged(
+ text: CharSequence,
+ start: Int,
+ lengthBefore: Int,
+ lengthAfter: Int
+ ) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter)
+ lastOnTextChanged = "${getTimestamp()}"
+ }
+
fun setLineSpacingScale(scale: Float) {
lineSpacingScale = scale
setLineSpacing(0f, lineSpacingScale)
@@ -370,7 +396,12 @@ class AnimatableClockView @JvmOverloads constructor(
pw.println(" measuredWidth=$measuredWidth")
pw.println(" measuredHeight=$measuredHeight")
pw.println(" singleLineInternal=$isSingleLineInternal")
+ pw.println(" lastTextUpdate=$lastTextUpdate")
+ pw.println(" lastOnTextChanged=$lastOnTextChanged")
+ pw.println(" lastInvalidate=$lastInvalidate")
pw.println(" lastMeasureCall=$lastMeasureCall")
+ pw.println(" lastDraw=$lastDraw")
+ pw.println(" lastTimeZoneChange=$lastTimeZoneChange")
pw.println(" currText=$text")
pw.println(" currTimeContextDesc=$contentDescription")
pw.println(" time=$time")
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index b29dc835a6f4..22bffda33918 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -49,6 +49,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
@@ -99,6 +100,7 @@ public class RotationButtonController {
private @WindowInsetsController.Behavior
int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
private int mNavBarMode;
+ private boolean mTaskBarVisible = false;
private boolean mSkipOverrideUserLockPrefsOnce;
private final int mLightIconColor;
private final int mDarkIconColor;
@@ -422,6 +424,7 @@ public class RotationButtonController {
}
public void onTaskbarStateChange(boolean visible, boolean stashed) {
+ mTaskBarVisible = visible;
if (getRotationButton() == null) {
return;
}
@@ -438,9 +441,12 @@ public class RotationButtonController {
* Return true when either the task bar is visible or it's in visual immersive mode.
*/
@SuppressLint("InlinedApi")
- private boolean canShowRotationButton() {
- return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT
- || isGesturalMode(mNavBarMode);
+ @VisibleForTesting
+ boolean canShowRotationButton() {
+ return mIsNavigationBarShowing
+ || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT
+ || isGesturalMode(mNavBarMode)
+ || mTaskBarVisible;
}
@DrawableRes
@@ -624,4 +630,3 @@ public class RotationButtonController {
}
}
}
-
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2f9bc1c59624..d75285283cbc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -54,6 +54,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -2018,12 +2019,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// in case authenticators aren't registered yet at this point:
mAuthController.addCallback(new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
+ public void onAllAuthenticatorsRegistered(
+ @BiometricAuthenticator.Modality int modality) {
mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
}
@Override
- public void onEnrollmentsChanged() {
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
}
});
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 06e1828ef9f4..d6974dfac570 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
@@ -29,6 +30,7 @@ import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.AnimatedStateListDrawable;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Process;
import android.os.VibrationAttributes;
@@ -701,13 +703,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsConfig();
+ public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsConfig();
+ }
}
@Override
- public void onEnrollmentsChanged() {
- updateUdfpsConfig();
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsConfig();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java b/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java
index 43b3929808b3..df65bcf9c10d 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -34,12 +35,12 @@ import javax.inject.Inject;
@SysUISingleton
public class ActivityIntentHelper {
- private final Context mContext;
+ private final PackageManager mPm;
@Inject
public ActivityIntentHelper(Context context) {
// TODO: inject a package manager, not a context.
- mContext = context;
+ mPm = context.getPackageManager();
}
/**
@@ -57,6 +58,15 @@ public class ActivityIntentHelper {
}
/**
+ * @see #wouldLaunchResolverActivity(Intent, int)
+ */
+ public boolean wouldPendingLaunchResolverActivity(PendingIntent intent, int currentUserId) {
+ ActivityInfo targetActivityInfo = getPendingTargetActivityInfo(intent, currentUserId,
+ false /* onlyDirectBootAware */);
+ return targetActivityInfo == null;
+ }
+
+ /**
* Returns info about the target Activity of a given intent, or null if the intent does not
* resolve to a specific component meeting the requirements.
*
@@ -68,19 +78,45 @@ public class ActivityIntentHelper {
*/
public ActivityInfo getTargetActivityInfo(Intent intent, int currentUserId,
boolean onlyDirectBootAware) {
- PackageManager packageManager = mContext.getPackageManager();
- int flags = PackageManager.MATCH_DEFAULT_ONLY;
+ int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA;
if (!onlyDirectBootAware) {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
}
- final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+ final List<ResolveInfo> appList = mPm.queryIntentActivitiesAsUser(
intent, flags, currentUserId);
if (appList.size() == 0) {
return null;
}
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- flags | PackageManager.GET_META_DATA, currentUserId);
+ if (appList.size() == 1) {
+ return appList.get(0).activityInfo;
+ }
+ ResolveInfo resolved = mPm.resolveActivityAsUser(intent, flags, currentUserId);
+ if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
+ return null;
+ } else {
+ return resolved.activityInfo;
+ }
+ }
+
+ /**
+ * @see #getTargetActivityInfo(Intent, int, boolean)
+ */
+ public ActivityInfo getPendingTargetActivityInfo(PendingIntent intent, int currentUserId,
+ boolean onlyDirectBootAware) {
+ int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA;
+ if (!onlyDirectBootAware) {
+ flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ }
+ final List<ResolveInfo> appList = intent.queryIntentComponents(flags);
+ if (appList.size() == 0) {
+ return null;
+ }
+ if (appList.size() == 1) {
+ return appList.get(0).activityInfo;
+ }
+ ResolveInfo resolved = mPm.resolveActivityAsUser(intent.getIntent(), flags, currentUserId);
if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
return null;
} else {
@@ -104,6 +140,17 @@ public class ActivityIntentHelper {
}
/**
+ * @see #wouldShowOverLockscreen(Intent, int)
+ */
+ public boolean wouldPendingShowOverLockscreen(PendingIntent intent, int currentUserId) {
+ ActivityInfo targetActivityInfo = getPendingTargetActivityInfo(intent,
+ currentUserId, false /* onlyDirectBootAware */);
+ return targetActivityInfo != null
+ && (targetActivityInfo.flags & (ActivityInfo.FLAG_SHOW_WHEN_LOCKED
+ | ActivityInfo.FLAG_SHOW_FOR_ALL_USERS)) > 0;
+ }
+
+ /**
* Determines if sending the given intent would result in starting an Intent resolver activity,
* instead of resolving to a specific component.
*
diff --git a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
index b0eaab97c5ac..fa9a83ec9913 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
@@ -25,7 +25,6 @@ import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.FeatureFlagUtils;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.systemui.util.NotificationChannels;
@@ -59,10 +58,8 @@ public final class GuestSessionNotification {
}
void createPersistentNotification(UserInfo userInfo, boolean isGuestFirstLogin) {
- if (!FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES)
- || !userInfo.isGuest()) {
- // we create a persistent notification only if enabled and only for guests
+ if (!userInfo.isGuest()) {
+ // we create a persistent notification only for guests
return;
}
String contentText;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 47ff59cfc281..282f25104c44 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -46,6 +46,7 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
@@ -156,25 +157,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
};
- private final IFingerprintAuthenticatorsRegisteredCallback
- mFingerprintAuthenticatorsRegisteredCallback =
- new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
- @Override
- public void onAllAuthenticatorsRegistered(
- List<FingerprintSensorPropertiesInternal> sensors) {
- mHandler.post(() -> handleAllFingerprintAuthenticatorsRegistered(sensors));
- }
- };
-
- private final BiometricStateListener mBiometricStateListener =
- new BiometricStateListener() {
- @Override
- public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
- mHandler.post(
- () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
- }
- };
-
@VisibleForTesting
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -249,8 +231,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
List<FingerprintSensorPropertiesInternal> sensors) {
mExecution.assertIsMainThread();
if (DEBUG) {
- Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString(
- sensors.toArray()));
+ Log.d(TAG, "handleAllFingerprintAuthenticatorsRegistered | sensors: "
+ + Arrays.toString(sensors.toArray()));
}
mAllFingerprintAuthenticatorsRegistered = true;
mFpProps = sensors;
@@ -292,15 +274,42 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mSidefpsController = mSidefpsControllerFactory.get();
}
- mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
+ mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mHandler.post(() -> handleEnrollmentsChanged(
+ TYPE_FINGERPRINT, userId, sensorId, hasEnrollments));
+ }
+ });
updateFingerprintLocation();
for (Callback cb : mCallbacks) {
- cb.onAllAuthenticatorsRegistered();
+ cb.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
}
}
- private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ private void handleAllFaceAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
+ mExecution.assertIsMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "handleAllFaceAuthenticatorsRegistered | sensors: " + Arrays.toString(
+ sensors.toArray()));
+ }
+
+ mFaceManager.registerBiometricStateListener(new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mHandler.post(() -> handleEnrollmentsChanged(
+ TYPE_FACE, userId, sensorId, hasEnrollments));
+ }
+ });
+
+ for (Callback cb : mCallbacks) {
+ cb.onAllAuthenticatorsRegistered(TYPE_FACE);
+ }
+ }
+
+ private void handleEnrollmentsChanged(@Modality int modality, int userId, int sensorId,
+ boolean hasEnrollments) {
mExecution.assertIsMainThread();
Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
+ ", hasEnrollments: " + hasEnrollments);
@@ -314,7 +323,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
}
for (Callback cb : mCallbacks) {
- cb.onEnrollmentsChanged();
+ cb.onEnrollmentsChanged(modality);
}
}
@@ -700,7 +709,26 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
if (mFingerprintManager != null) {
mFingerprintManager.addAuthenticatorsRegisteredCallback(
- mFingerprintAuthenticatorsRegisteredCallback);
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ mHandler.post(() ->
+ handleAllFingerprintAuthenticatorsRegistered(sensors));
+ }
+ });
+ }
+ if (mFaceManager != null) {
+ mFaceManager.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ mHandler.post(() ->
+ handleAllFaceAuthenticatorsRegistered(sensors));
+ }
+ }
+ );
}
mStableDisplaySize = mDisplayManager.getStableDisplaySize();
@@ -1116,13 +1144,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
* Called when authenticators are registered. If authenticators are already
* registered before this call, this callback will never be triggered.
*/
- default void onAllAuthenticatorsRegistered() {}
+ default void onAllAuthenticatorsRegistered(@Modality int modality) {}
/**
- * Called when UDFPS enrollments have changed. This is called after boot and on changes to
+ * Called when enrollments have changed. This is called after boot and on changes to
* enrollment.
*/
- default void onEnrollmentsChanged() {}
+ default void onEnrollmentsChanged(@Modality int modality) {}
/**
* Called when the biometric prompt starts showing.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 38fab8ffbfad..fd3f6007d8a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -308,7 +308,7 @@ class AuthRippleController @Inject constructor(
private val authControllerCallback =
object : AuthController.Callback {
- override fun onAllAuthenticatorsRegistered() {
+ override fun onAllAuthenticatorsRegistered(modality: Int) {
updateUdfpsDependentParams()
updateSensorLocation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 2dadf573fd12..e549a96079bd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -241,7 +241,6 @@ public abstract class SystemUIModule {
notifCollection,
notifPipeline,
sysUiState,
- dumpManager,
sysuiMainExecutor));
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index a9e310d25f9c..7da2cf150147 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -16,12 +16,15 @@
package com.android.systemui.doze;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
@@ -232,13 +235,17 @@ public class DozeScreenState implements DozeMachine.Part {
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsController();
+ public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsController();
+ }
}
@Override
- public void onEnrollmentsChanged() {
- updateUdfpsController();
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsController();
+ }
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index da6c163b1eea..997a6e554364 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,8 @@
package com.android.systemui.doze;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP;
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
@@ -29,6 +31,7 @@ import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.display.AmbientDisplayConfiguration;
import android.net.Uri;
import android.os.Handler;
@@ -835,13 +838,17 @@ public class DozeSensors {
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsEnrolled();
+ public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsEnrolled();
+ }
}
@Override
- public void onEnrollmentsChanged() {
- updateUdfpsEnrolled();
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsEnrolled();
+ }
}
private void updateUdfpsEnrolled() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 7e4a108aadf1..823255c38a84 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -113,7 +113,7 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
}
void setExtraStatusBarItemViews(List<View> views) {
- mSystemStatusViewGroup.removeAllViews();
+ removeAllStatusBarItemViews();
views.forEach(view -> mSystemStatusViewGroup.addView(view));
}
@@ -121,4 +121,8 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
final View statusIcon = findViewById(resId);
return Objects.requireNonNull(statusIcon);
}
+
+ void removeAllStatusBarItemViews() {
+ mSystemStatusViewGroup.removeAllViews();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 65cfae1ac14b..6f505504b186 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -192,6 +192,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mDreamOverlayNotificationCountProvider.ifPresent(
provider -> provider.removeCallback(mNotificationCountCallback));
mStatusBarItemsProvider.removeCallback(mStatusBarItemsProviderCallback);
+ mView.removeAllStatusBarItemViews();
mTouchInsetSession.clear();
mIsAttached = false;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
index 83249aa324d1..bbcab60d7ba2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
@@ -69,7 +69,7 @@ public class ComplicationTypesUpdater extends CoreStartable {
};
mSecureSettings.registerContentObserverForUser(
- Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
+ Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED,
settingsObserver,
UserHandle.myUserId());
settingsObserver.onChange(false);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 1f356cb3d18c..677f0a2e288b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -154,7 +154,11 @@ public class Flags {
public static final ReleasedFlag STATUS_BAR_LETTERBOX_APPEARANCE =
new ReleasedFlag(603, false);
- public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE = new UnreleasedFlag(604, true);
+ public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE_BACKEND =
+ new UnreleasedFlag(604, true);
+
+ public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE_FRONTEND =
+ new UnreleasedFlag(605, true);
/***************************************/
// 700 - dialer/calls
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 19c6249a12c0..c4e3d4e4c1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -251,9 +251,21 @@ object KeyguardBottomAreaViewBinder {
Utils.getColorAttr(view.context, com.android.internal.R.attr.colorSurface)
view.contentDescription = view.context.getString(viewModel.contentDescriptionResourceId)
- view.setOnClickListener {
+ view.isClickable = viewModel.isClickable
+ if (viewModel.isClickable) {
+ view.setOnClickListener(OnClickListener(viewModel, falsingManager))
+ } else {
+ view.setOnClickListener(null)
+ }
+ }
+
+ private class OnClickListener(
+ private val viewModel: KeyguardQuickAffordanceViewModel,
+ private val falsingManager: FalsingManager,
+ ) : View.OnClickListener {
+ override fun onClick(view: View) {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return@setOnClickListener
+ return
}
if (viewModel.configKey != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 01d5e5c493ce..e3ebac60febb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import androidx.annotation.VisibleForTesting
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -37,6 +38,23 @@ constructor(
private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
private val burnInHelperWrapper: BurnInHelperWrapper,
) {
+ /**
+ * Whether quick affordances are "opaque enough" to be considered visible to and interactive by
+ * the user. If they are not interactive, user input should not be allowed on them.
+ *
+ * Note that there is a margin of error, where we allow very, very slightly transparent views to
+ * be considered "fully opaque" for the purpose of being interactive. This is to accommodate the
+ * error margin of floating point arithmetic.
+ *
+ * A view that is visible but with an alpha of less than our threshold either means it's not
+ * fully done fading in or is fading/faded out. Either way, it should not be
+ * interactive/clickable unless "fully opaque" to avoid issues like in b/241830987.
+ */
+ private val areQuickAffordancesFullyOpaque: Flow<Boolean> =
+ bottomAreaInteractor.alpha
+ .map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD }
+ .distinctUntilChanged()
+
/** An observable for the view-model of the "start button" quick affordance. */
val startButton: Flow<KeyguardQuickAffordanceViewModel> =
button(KeyguardQuickAffordancePosition.BOTTOM_START)
@@ -77,14 +95,19 @@ constructor(
return combine(
quickAffordanceInteractor.quickAffordance(position),
bottomAreaInteractor.animateDozingTransitions.distinctUntilChanged(),
- ) { model, animateReveal ->
- model.toViewModel(animateReveal)
+ areQuickAffordancesFullyOpaque,
+ ) { model, animateReveal, isFullyOpaque ->
+ model.toViewModel(
+ animateReveal = animateReveal,
+ isClickable = isFullyOpaque,
+ )
}
.distinctUntilChanged()
}
private fun KeyguardQuickAffordanceModel.toViewModel(
animateReveal: Boolean,
+ isClickable: Boolean,
): KeyguardQuickAffordanceViewModel {
return when (this) {
is KeyguardQuickAffordanceModel.Visible ->
@@ -100,8 +123,20 @@ constructor(
animationController = parameters.animationController,
)
},
+ isClickable = isClickable,
)
is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
}
}
+
+ companion object {
+ // We select a value that's less than 1.0 because we want floating point math precision to
+ // not be a factor in determining whether the affordance UI is fully opaque. The number we
+ // choose needs to be close enough 1.0 such that the user can't easily tell the difference
+ // between the UI with an alpha at the threshold and when the alpha is 1.0. At the same
+ // time, we don't want the number to be too close to 1.0 such that there is a chance that we
+ // never treat the affordance UI as "fully opaque" as that would risk making it forever not
+ // clickable.
+ @VisibleForTesting const val AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD = 0.95f
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index 985ab623764a..b1de27d262cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -31,6 +31,7 @@ data class KeyguardQuickAffordanceViewModel(
val icon: ContainedDrawable = ContainedDrawable.WithResource(0),
@StringRes val contentDescriptionResourceId: Int = 0,
val onClicked: (OnClickedParameters) -> Unit = {},
+ val isClickable: Boolean = false,
) {
data class OnClickedParameters(
val configKey: KClass<out KeyguardQuickAffordanceConfig>,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index e360d10d9362..ee5956105b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -16,6 +16,7 @@
package com.android.systemui.media.dialog;
+import android.annotation.DrawableRes;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
@@ -42,9 +43,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private ViewGroup mConnectedItem;
- private boolean mIncludeDynamicGroup;
-
public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
setHasStableIds(true);
@@ -102,141 +100,90 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
super.onBind(device, topMargin, bottomMargin, position);
boolean isMutingExpectedDeviceExist = mController.hasMutingExpectedDevice();
- final boolean currentlyConnected = !mIncludeDynamicGroup
- && isCurrentlyConnected(device);
+ final boolean currentlyConnected = isCurrentlyConnected(device);
boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE;
- if (currentlyConnected) {
- mConnectedItem = mContainerLayout;
- }
- mCheckBox.setVisibility(View.GONE);
- mStatusIcon.setVisibility(View.GONE);
- mEndTouchArea.setVisibility(View.GONE);
- mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mContainerLayout.setOnClickListener(null);
- mContainerLayout.setContentDescription(null);
- mTitleText.setTextColor(mController.getColorItemContent());
- mSubTitleText.setTextColor(mController.getColorItemContent());
- mTwoLineTitleText.setTextColor(mController.getColorItemContent());
- mSeekBar.getProgressDrawable().setColorFilter(
- new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
- PorterDuff.Mode.SRC_IN));
if (mCurrentActivePosition == position) {
mCurrentActivePosition = -1;
}
- if (mController.isTransferring()) {
+ if (mController.isAnyDeviceTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
setUpDeviceIcon(device);
- mProgressBar.getIndeterminateDrawable().setColorFilter(
- new PorterDuffColorFilter(
- mController.getColorItemContent(),
- PorterDuff.Mode.SRC_IN));
- setSingleLineLayout(getItemTitle(device), true /* bFocused */,
- false /* showSeekBar*/,
- true /* showProgressBar */, false /* showStatus */);
+ updateProgressBarColor();
+ setSingleLineLayout(getItemTitle(device), false /* showSeekBar*/,
+ true /* showProgressBar */, false /* showCheckBox */,
+ false /* showEndTouchArea */);
} else {
setUpDeviceIcon(device);
- setSingleLineLayout(getItemTitle(device), false /* bFocused */);
+ setSingleLineLayout(getItemTitle(device));
}
} else {
// Set different layout for each device
if (device.isMutingExpectedDevice()
&& !mController.isCurrentConnectedDeviceRemote()) {
- mTitleIcon.setImageDrawable(
- mContext.getDrawable(R.drawable.media_output_icon_volume));
- mTitleIcon.setColorFilter(mController.getColorItemContent());
- mTitleText.setTextColor(mController.getColorItemContent());
- setSingleLineLayout(getItemTitle(device), true /* bFocused */,
- false /* showSeekBar */,
- false /* showProgressBar */, false /* showStatus */);
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
initMutingExpectedDevice();
mCurrentActivePosition = position;
- mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+ updateContainerClickListener(v -> onItemClick(v, device));
+ setSingleLineLayout(getItemTitle(device));
} else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
setUpDeviceIcon(device);
- mStatusIcon.setImageDrawable(
- mContext.getDrawable(R.drawable.media_output_status_failed));
- mStatusIcon.setColorFilter(mController.getColorItemContent());
- setTwoLineLayout(device, false /* bFocused */,
- false /* showSeekBar */, false /* showProgressBar */,
- true /* showSubtitle */, true /* showStatus */);
+ updateConnectionFailedStatusIcon();
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
- mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+ updateContainerClickListener(v -> onItemClick(v, device));
+ setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */,
+ false /* showProgressBar */, true /* showSubtitle */,
+ true /* showStatus */);
} else if (device.getState() == MediaDeviceState.STATE_GROUPING) {
setUpDeviceIcon(device);
- mProgressBar.getIndeterminateDrawable().setColorFilter(
- new PorterDuffColorFilter(
- mController.getColorItemContent(),
- PorterDuff.Mode.SRC_IN));
- setSingleLineLayout(getItemTitle(device), true /* bFocused */,
- false /* showSeekBar*/,
- true /* showProgressBar */, false /* showStatus */);
+ updateProgressBarColor();
+ setSingleLineLayout(getItemTitle(device), false /* showSeekBar*/,
+ true /* showProgressBar */, false /* showCheckBox */,
+ false /* showEndTouchArea */);
} else if (mController.getSelectedMediaDevice().size() > 1
&& isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
boolean isDeviceDeselectable = isDeviceIncluded(
mController.getDeselectableMediaDevice(), device);
- mTitleText.setTextColor(mController.getColorItemContent());
- mTitleIcon.setImageDrawable(
- mContext.getDrawable(R.drawable.media_output_icon_volume));
- mTitleIcon.setColorFilter(mController.getColorItemContent());
- setSingleLineLayout(getItemTitle(device), true /* bFocused */,
- true /* showSeekBar */,
- false /* showProgressBar */, false /* showStatus */);
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
+ updateGroupableCheckBox(true, isDeviceDeselectable, device);
+ updateEndClickArea(device, isDeviceDeselectable);
setUpContentDescriptionForView(mContainerLayout, false, device);
- mCheckBox.setOnCheckedChangeListener(null);
- mCheckBox.setVisibility(View.VISIBLE);
- mCheckBox.setChecked(true);
- mCheckBox.setOnCheckedChangeListener(isDeviceDeselectable
- ? (buttonView, isChecked) -> onGroupActionTriggered(false, device)
- : null);
- mCheckBox.setEnabled(isDeviceDeselectable);
- setCheckBoxColor(mCheckBox, mController.getColorItemContent());
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, true /* showCheckBox */,
+ true /* showEndTouchArea */);
initSeekbar(device, isCurrentSeekbarInvisible);
- mEndTouchArea.setVisibility(View.VISIBLE);
- mEndTouchArea.setOnClickListener(null);
- mEndTouchArea.setOnClickListener(
- isDeviceDeselectable ? (v) -> mCheckBox.performClick() : null);
- mEndTouchArea.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- setUpContentDescriptionForView(mEndTouchArea, true, device);
} else if (!mController.hasAdjustVolumeUserRestriction()
&& currentlyConnected) {
if (isMutingExpectedDeviceExist
&& !mController.isCurrentConnectedDeviceRemote()) {
// mark as disconnected and set special click listener
setUpDeviceIcon(device);
- setSingleLineLayout(getItemTitle(device), false /* bFocused */);
- mContainerLayout.setOnClickListener(v -> cancelMuteAwaitConnection());
+ updateContainerClickListener(v -> cancelMuteAwaitConnection());
+ setSingleLineLayout(getItemTitle(device));
} else {
- mTitleIcon.setImageDrawable(
- mContext.getDrawable(R.drawable.media_output_icon_volume));
- mTitleIcon.setColorFilter(mController.getColorItemContent());
- mTitleText.setTextColor(mController.getColorItemContent());
- setSingleLineLayout(getItemTitle(device), true /* bFocused */,
- true /* showSeekBar */,
- false /* showProgressBar */, false /* showStatus */);
- initSeekbar(device, isCurrentSeekbarInvisible);
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
setUpContentDescriptionForView(mContainerLayout, false, device);
mCurrentActivePosition = position;
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, false /* showCheckBox */,
+ false /* showEndTouchArea */);
+ initSeekbar(device, isCurrentSeekbarInvisible);
}
} else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
setUpDeviceIcon(device);
- mCheckBox.setOnCheckedChangeListener(null);
- mCheckBox.setVisibility(View.VISIBLE);
- mCheckBox.setChecked(false);
- mCheckBox.setOnCheckedChangeListener(
- (buttonView, isChecked) -> onGroupActionTriggered(true, device));
- mEndTouchArea.setVisibility(View.VISIBLE);
- mContainerLayout.setOnClickListener(v -> onGroupActionTriggered(true, device));
- setCheckBoxColor(mCheckBox, mController.getColorItemContent());
- setSingleLineLayout(getItemTitle(device), false /* bFocused */,
- false /* showSeekBar */,
- false /* showProgressBar */, false /* showStatus */);
+ updateGroupableCheckBox(false, true, device);
+ updateContainerClickListener(v -> onGroupActionTriggered(true, device));
+ setSingleLineLayout(getItemTitle(device), false /* showSeekBar */,
+ false /* showProgressBar */, true /* showCheckBox */,
+ true /* showEndTouchArea */);
} else {
setUpDeviceIcon(device);
- setSingleLineLayout(getItemTitle(device), false /* bFocused */);
- mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+ setSingleLineLayout(getItemTitle(device));
+ updateContainerClickListener(v -> onItemClick(v, device));
}
}
}
@@ -248,15 +195,56 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
ColorStateList(states, colors));
}
+ private void updateConnectionFailedStatusIcon() {
+ mStatusIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.media_output_status_failed));
+ mStatusIcon.setColorFilter(mController.getColorItemContent());
+ }
+
+ private void updateProgressBarColor() {
+ mProgressBar.getIndeterminateDrawable().setColorFilter(
+ new PorterDuffColorFilter(
+ mController.getColorItemContent(),
+ PorterDuff.Mode.SRC_IN));
+ }
+
+ public void updateEndClickArea(MediaDevice device, boolean isDeviceDeselectable) {
+ mEndTouchArea.setOnClickListener(null);
+ mEndTouchArea.setOnClickListener(
+ isDeviceDeselectable ? (v) -> mCheckBox.performClick() : null);
+ mEndTouchArea.setImportantForAccessibility(
+ View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ setUpContentDescriptionForView(mEndTouchArea, true, device);
+ }
+
+ private void updateGroupableCheckBox(boolean isSelected, boolean isGroupable,
+ MediaDevice device) {
+ mCheckBox.setOnCheckedChangeListener(null);
+ mCheckBox.setChecked(isSelected);
+ mCheckBox.setOnCheckedChangeListener(
+ isGroupable ? (buttonView, isChecked) -> onGroupActionTriggered(!isSelected,
+ device) : null);
+ mCheckBox.setEnabled(isGroupable);
+ setCheckBoxColor(mCheckBox, mController.getColorItemContent());
+ }
+
+ private void updateTitleIcon(@DrawableRes int id, int color) {
+ mTitleIcon.setImageDrawable(mContext.getDrawable(id));
+ mTitleIcon.setColorFilter(color);
+ }
+
+ private void updateContainerClickListener(View.OnClickListener listener) {
+ mContainerLayout.setOnClickListener(listener);
+ }
+
@Override
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mTitleText.setTextColor(mController.getColorItemContent());
mCheckBox.setVisibility(View.GONE);
- setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
- false /* bFocused */);
- final Drawable d = mContext.getDrawable(R.drawable.ic_add);
- mTitleIcon.setImageDrawable(d);
+ setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new));
+ final Drawable addDrawable = mContext.getDrawable(R.drawable.ic_add);
+ mTitleIcon.setImageDrawable(addDrawable);
mTitleIcon.setColorFilter(mController.getColorItemContent());
mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
}
@@ -273,7 +261,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
private void onItemClick(View view, MediaDevice device) {
- if (mController.isTransferring()) {
+ if (mController.isAnyDeviceTransferring()) {
return;
}
if (isCurrentlyConnected(device)) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 43b0287c164e..3f7b2261ea52 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -63,8 +63,6 @@ public abstract class MediaOutputBaseAdapter extends
protected final MediaOutputController mController;
- private int mMargin;
-
Context mContext;
View mHolderView;
boolean mIsDragging;
@@ -82,8 +80,6 @@ public abstract class MediaOutputBaseAdapter extends
public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) {
mContext = viewGroup.getContext();
- mMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.media_output_dialog_list_margin);
mHolderView = LayoutInflater.from(mContext).inflate(R.layout.media_output_list_item,
viewGroup, false);
@@ -168,16 +164,28 @@ public abstract class MediaOutputBaseAdapter extends
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
mDeviceId = device.getId();
+ mCheckBox.setVisibility(View.GONE);
+ mStatusIcon.setVisibility(View.GONE);
+ mEndTouchArea.setVisibility(View.GONE);
+ mEndTouchArea.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mContainerLayout.setOnClickListener(null);
+ mContainerLayout.setContentDescription(null);
+ mTitleText.setTextColor(mController.getColorItemContent());
+ mSubTitleText.setTextColor(mController.getColorItemContent());
+ mTwoLineTitleText.setTextColor(mController.getColorItemContent());
+ mSeekBar.getProgressDrawable().setColorFilter(
+ new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
+ PorterDuff.Mode.SRC_IN));
}
abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin);
- void setSingleLineLayout(CharSequence title, boolean bFocused) {
- setSingleLineLayout(title, bFocused, false, false, false);
+ void setSingleLineLayout(CharSequence title) {
+ setSingleLineLayout(title, false, false, false, false);
}
- void setSingleLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
- boolean showProgressBar, boolean showStatus) {
+ void setSingleLineLayout(CharSequence title, boolean showSeekBar,
+ boolean showProgressBar, boolean showCheckBox, boolean showEndTouchArea) {
mTwoLineLayout.setVisibility(View.GONE);
boolean isActive = showSeekBar || showProgressBar;
if (!mCornerAnimator.isRunning()) {
@@ -188,10 +196,6 @@ public abstract class MediaOutputBaseAdapter extends
.mutate() : mContext.getDrawable(
R.drawable.media_output_item_background)
.mutate();
- backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
- isActive ? mController.getColorConnectedItemBackground()
- : mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
mItemLayout.setBackground(backgroundDrawable);
if (showSeekBar) {
final ClipDrawable clipDrawable =
@@ -201,27 +205,21 @@ public abstract class MediaOutputBaseAdapter extends
(GradientDrawable) clipDrawable.getDrawable();
progressDrawable.setCornerRadius(mController.getActiveRadius());
}
- } else {
- mItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(
- isActive ? mController.getColorConnectedItemBackground()
- : mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
}
+ mItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(
+ isActive ? mController.getColorConnectedItemBackground()
+ : mController.getColorItemBackground(),
+ PorterDuff.Mode.SRC_IN));
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSeekBar.setAlpha(1);
mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
if (!showSeekBar) {
mSeekBar.resetVolume();
}
- mStatusIcon.setVisibility(showStatus ? View.VISIBLE : View.GONE);
mTitleText.setText(title);
mTitleText.setVisibility(View.VISIBLE);
- }
-
- void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
- boolean showProgressBar, boolean showSubtitle) {
- setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle,
- false);
+ mCheckBox.setVisibility(showCheckBox ? View.VISIBLE : View.GONE);
+ mEndTouchArea.setVisibility(showEndTouchArea ? View.VISIBLE : View.GONE);
}
void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
@@ -230,12 +228,6 @@ public abstract class MediaOutputBaseAdapter extends
showStatus);
}
- void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
- boolean showProgressBar, boolean showSubtitle) {
- setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle,
- false);
- }
-
private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle,
boolean showStatus) {
@@ -254,20 +246,11 @@ public abstract class MediaOutputBaseAdapter extends
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
mTwoLineTitleText.setTranslationY(0);
- if (device == null) {
- mTwoLineTitleText.setText(title);
- } else {
- mTwoLineTitleText.setText(getItemTitle(device));
- }
-
- if (bFocused) {
- mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
- com.android.internal.R.string.config_headlineFontFamilyMedium),
- Typeface.NORMAL));
- } else {
- mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
- com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
- }
+ mTwoLineTitleText.setText(device == null ? title : getItemTitle(device));
+ mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+ bFocused ? com.android.internal.R.string.config_headlineFontFamilyMedium
+ : com.android.internal.R.string.config_headlineFontFamily),
+ Typeface.NORMAL));
}
void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
@@ -327,35 +310,6 @@ public abstract class MediaOutputBaseAdapter extends
mItemLayout.setBackground(backgroundDrawable);
}
- void initSessionSeekbar() {
- disableSeekBar();
- mSeekBar.setMax(mController.getSessionVolumeMax());
- mSeekBar.setMin(0);
- final int currentVolume = mController.getSessionVolume();
- if (mSeekBar.getProgress() != currentVolume) {
- mSeekBar.setProgress(currentVolume, true);
- }
- mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (!fromUser) {
- return;
- }
- mController.adjustSessionVolume(progress);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- mIsDragging = true;
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- mIsDragging = false;
- }
- });
- }
-
private void animateCornerAndVolume(int fromProgress, int toProgress) {
final GradientDrawable layoutBackgroundDrawable =
(GradientDrawable) mItemLayout.getBackground();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f7d80e0b1dbd..96817c9bf32d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -411,7 +411,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
device.getId());
boolean isSelectedDeviceInGroup = getSelectedMediaDevice().size() > 1
&& getSelectedMediaDevice().contains(device);
- return (!hasAdjustVolumeUserRestriction() && isConnected && !isTransferring())
+ return (!hasAdjustVolumeUserRestriction() && isConnected && !isAnyDeviceTransferring())
|| isSelectedDeviceInGroup;
}
@@ -708,7 +708,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
UserHandle.of(UserHandle.myUserId()));
}
- boolean isTransferring() {
+ boolean isAnyDeviceTransferring() {
synchronized (mMediaDevicesLock) {
for (MediaDevice device : mMediaDevices) {
if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 2278938c398e..3a0ac1b7d9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -226,7 +226,7 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
- return appIconView.contentDescription.toString()
+ return appIconView.contentDescription
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 196ea222e50d..00a22f20e94d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -141,12 +141,13 @@ class MediaTttChipControllerReceiver @Inject constructor(
override fun updateChipView(newChipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
super.updateChipView(newChipInfo, currentChipView)
- setIcon(
+ val iconName = setIcon(
currentChipView,
newChipInfo.routeInfo.clientPackageName,
newChipInfo.appIconDrawableOverride,
newChipInfo.appNameOverride
)
+ currentChipView.contentDescription = iconName
}
override fun animateChipIn(chipView: ViewGroup) {
@@ -159,6 +160,8 @@ class MediaTttChipControllerReceiver @Inject constructor(
.alpha(1f)
.setDuration(5.frames)
.start()
+ // Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
+ appIconView.postOnAnimation { chipView.requestAccessibilityFocus() }
startRipple(chipView.requireViewById(R.id.ripple))
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index eeb1010693fc..2a6cf66995ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -80,7 +80,8 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
FeatureFlags featureFlags,
VariableDateViewController.Factory variableDateViewControllerFactory,
BatteryMeterViewController batteryMeterViewController,
- StatusBarContentInsetsProvider statusBarContentInsetsProvider) {
+ StatusBarContentInsetsProvider statusBarContentInsetsProvider,
+ StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory) {
super(view);
mPrivacyIconsController = headerPrivacyIconsController;
mStatusBarIconController = statusBarIconController;
@@ -103,7 +104,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mView.requireViewById(R.id.date_clock)
);
- mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags);
+ mIconManager = tintedIconManagerFactory.create(mIconContainer);
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mColorExtractor = colorExtractor;
mOnColorsChangedListener = (extractor, which) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index 948fb1428780..60380064e098 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -129,6 +129,15 @@ class QSLogger @Inject constructor(
})
}
+ fun logInternetTileUpdate(lastType: Int, callback: String) {
+ log(VERBOSE, {
+ int1 = lastType
+ str1 = callback
+ }, {
+ "mLastTileState=$int1, Callback=$str1."
+ })
+ }
+
fun logTileUpdated(tileSpec: String, state: QSTile.State) {
log(VERBOSE, {
str1 = tileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 170fecf1c8fd..ae464771bf48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -369,11 +369,10 @@ public class InternetTile extends QSTileImpl<SignalState> {
mWifiInfo.mNoDefaultNetwork = noDefaultNetwork;
mWifiInfo.mNoValidatedNetwork = noValidatedNetwork;
mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
- if (mLastTileState == LAST_STATE_WIFI) {
- refreshState(mWifiInfo);
- } else {
- refreshState(mCellularInfo);
+ if (!noDefaultNetwork) {
+ return;
}
+ refreshState(mWifiInfo);
}
@Override
@@ -388,6 +387,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Override
protected void handleUpdateState(SignalState state, Object arg) {
+ mQSLogger.logInternetTileUpdate(mLastTileState, arg == null ? "null" : arg.toString());
if (arg instanceof CellularCallbackInfo) {
mLastTileState = LAST_STATE_CELLULAR;
handleUpdateCellularState(state, arg);
@@ -605,4 +605,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
pw.print(" "); pw.println("mLastTileState=" + mLastTileState);
pw.print(" "); pw.println("mSignalCallback=" + mSignalCallback.toString());
}
+
+ // For testing usage only.
+ protected int getLastTileState() {
+ return mLastTileState;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index a8993bc274e4..8b37aab87665 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -70,7 +70,7 @@ import javax.inject.Inject;
public class TakeScreenshotService extends Service {
private static final String TAG = logTag(TakeScreenshotService.class);
- private ScreenshotController mScreenshot;
+ private final ScreenshotController mScreenshot;
private final UserManager mUserManager;
private final DevicePolicyManager mDevicePolicyManager;
@@ -152,10 +152,7 @@ public class TakeScreenshotService extends Service {
if (DEBUG_SERVICE) {
Log.d(TAG, "onUnbind");
}
- if (mScreenshot != null) {
- mScreenshot.removeWindow();
- mScreenshot = null;
- }
+ mScreenshot.removeWindow();
unregisterReceiver(mCloseSystemDialogs);
return false;
}
@@ -163,10 +160,7 @@ public class TakeScreenshotService extends Service {
@Override
public void onDestroy() {
super.onDestroy();
- if (mScreenshot != null) {
- mScreenshot.onDestroy();
- mScreenshot = null;
- }
+ mScreenshot.onDestroy();
if (DEBUG_SERVICE) {
Log.d(TAG, "onDestroy");
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 5e908d9cd29f..1558ac533137 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -76,6 +76,6 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* Notifies that the current user's profiles have changed.
*/
@JvmDefault
- fun onProfilesChanged(profiles: List<UserInfo>) {}
+ fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 0f9ac360cbe1..fab70fc2eebd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -77,6 +77,7 @@ import javax.inject.Named
class LargeScreenShadeHeaderController @Inject constructor(
@Named(LARGE_SCREEN_SHADE_HEADER) private val header: View,
private val statusBarIconController: StatusBarIconController,
+ private val tintedIconManagerFactory: StatusBarIconController.TintedIconManager.Factory,
private val privacyIconsController: HeaderPrivacyIconsController,
private val insetsProvider: StatusBarContentInsetsProvider,
private val configurationController: ConfigurationController,
@@ -259,7 +260,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
batteryMeterViewController.ignoreTunerUpdates()
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
- iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
+ iconManager = tintedIconManagerFactory.create(iconContainer)
iconManager.setTint(
Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7a4c87766114..9ca09ed6d70e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -54,7 +54,6 @@ import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
@@ -3752,13 +3751,12 @@ public final class NotificationPanelViewController extends PanelViewController {
*
* @param dozing {@code true} when dozing.
* @param animate if transition should be animated.
- * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
*/
- public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
+ public void setDozing(boolean dozing, boolean animate) {
if (dozing == mDozing) return;
mView.setDozing(dozing);
mDozing = dozing;
- mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation);
+ mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
mKeyguardBottomArea.setDozing(mDozing, animate);
mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate);
mKeyguardStatusBarViewController.setDozing(mDozing);
@@ -4683,7 +4681,7 @@ public final class NotificationPanelViewController extends PanelViewController {
* change.
*/
public void showAodUi() {
- setDozing(true /* dozing */, false /* animate */, null);
+ setDozing(true /* dozing */, false /* animate */);
mStatusBarStateController.setUpcomingState(KEYGUARD);
mEntryManager.updateNotifications("showAodUi");
mStatusBarStateListener.onStateChanged(KEYGUARD);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 0898d6329f75..a72b7f167d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -24,6 +24,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -49,7 +50,8 @@ public abstract class AlertingNotificationManager {
protected int mMinimumDisplayTime;
protected int mAutoDismissNotificationDecay;
- private final Handler mHandler;
+ @VisibleForTesting
+ public Handler mHandler;
/**
* Called when posting a new notification that should alert the user and appear on screen.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt
new file mode 100644
index 000000000000..4d53064d047d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+
+/**
+ * A temporary base class that's shared between our old status bar wifi view implementation
+ * ([StatusBarWifiView]) and our new status bar wifi view implementation
+ * ([ModernStatusBarWifiView]).
+ *
+ * Once our refactor is over, we should be able to delete this go-between class and the old view
+ * class.
+ */
+abstract class BaseStatusBarWifiView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttrs: Int = 0,
+) : FrameLayout(context, attrs, defStyleAttrs), StatusIconDisplayable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 0280e0b729a9..c04bc8289f81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -303,11 +303,6 @@ public class StatusBarStateControllerImpl implements
}
@Override
- public void setDozeAmount(float dozeAmount, boolean animated) {
- setAndInstrumentDozeAmount(null, dozeAmount, animated);
- }
-
- @Override
public void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated) {
if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
if (animated && mDozeAmountTarget == dozeAmount) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index a6986d797833..5aee62e3e89f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -28,7 +28,6 @@ import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -41,8 +40,7 @@ import java.util.ArrayList;
/**
* Start small: StatusBarWifiView will be able to layout from a WifiIconState
*/
-public class StatusBarWifiView extends FrameLayout implements DarkReceiver,
- StatusIconDisplayable {
+public class StatusBarWifiView extends BaseStatusBarWifiView implements DarkReceiver {
private static final String TAG = "StatusBarWifiView";
/// Used to show etc dots
@@ -80,11 +78,6 @@ public class StatusBarWifiView extends FrameLayout implements DarkReceiver,
super(context, attrs, defStyleAttr);
}
- public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
public void setSlot(String slot) {
mSlot = slot;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 2b3190159ecd..2cc77384fb2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -99,14 +99,6 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
boolean setIsDozing(boolean isDozing);
/**
- * Changes the current doze amount.
- *
- * @param dozeAmount New doze/dark amount.
- * @param animated If change should be animated or not. This will cancel current animations.
- */
- void setDozeAmount(float dozeAmount, boolean animated);
-
- /**
* Changes the current doze amount, also starts the
* {@link com.android.internal.jank.InteractionJankMonitor InteractionJankMonitor} as possible.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
index 3c449ad270ef..7097568c4adf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
@@ -23,7 +23,7 @@ import com.android.settingslib.SignalIcon.IconGroup;
/** */
public class WifiIcons {
- static final int[] WIFI_FULL_ICONS = {
+ public static final int[] WIFI_FULL_ICONS = {
com.android.internal.R.drawable.ic_wifi_signal_0,
com.android.internal.R.drawable.ic_wifi_signal_1,
com.android.internal.R.drawable.ic_wifi_signal_2,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9ad906c83e10..855390d75ff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -20,7 +20,6 @@ import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -236,11 +235,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
private boolean mIsHeadsUp;
- /**
- * Whether or not the notification should be redacted on the lock screen, i.e has sensitive
- * content which should be redacted on the lock screen.
- */
- private boolean mNeedsRedaction;
private boolean mLastChronometerRunning = true;
private ViewStub mChildrenContainerStub;
private GroupMembershipManager mGroupMembershipManager;
@@ -1502,23 +1496,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mUseIncreasedHeadsUpHeight = use;
}
- /** @deprecated TODO: Remove this when the old pipeline code is removed. */
- @Deprecated
- public void setNeedsRedaction(boolean needsRedaction) {
- if (mNeedsRedaction != needsRedaction) {
- mNeedsRedaction = needsRedaction;
- if (!isRemoved()) {
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- if (needsRedaction) {
- params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
- } else {
- params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
- }
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
- }
- }
- }
-
public interface ExpansionLogger {
void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 952bafbe6eb3..79d883b32a98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -42,7 +42,6 @@ import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.provider.Settings;
@@ -4405,8 +4404,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* See {@link AmbientState#setDozing}.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setDozing(boolean dozing, boolean animate,
- @Nullable PointF touchWakeUpScreenLocation) {
+ public void setDozing(boolean dozing, boolean animate) {
if (mAmbientState.isDozing() == dozing) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 9998fe41b775..3f6586c37b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -33,7 +33,6 @@ import static com.android.systemui.statusbar.phone.NotificationIconAreaControlle
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
-import android.graphics.PointF;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1235,8 +1234,8 @@ public class NotificationStackScrollLayoutController {
mView.setAnimationsEnabled(enabled);
}
- public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
- mView.setDozing(dozing, animate, wakeUpTouchLocation);
+ public void setDozing(boolean dozing, boolean animate) {
+ mView.setDozing(dozing, animate);
}
public void setPulsing(boolean pulsing, boolean animatePulse) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b0a5adb62339..e444e0a1e2c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -67,7 +67,6 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Point;
-import android.graphics.PointF;
import android.hardware.devicestate.DeviceStateManager;
import android.metrics.LogMaker;
import android.net.Uri;
@@ -446,7 +445,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
- private PointF mWakeUpTouchLocation;
private LightRevealScrim mLightRevealScrim;
private PowerButtonReveal mPowerButtonReveal;
@@ -603,8 +601,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
private int mLastCameraLaunchSource;
protected PowerManager.WakeLock mGestureWakeLock;
- private final int[] mTmpInt2 = new int[2];
-
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
private boolean mIsLaunchingActivityOverLockscreen;
@@ -1430,16 +1426,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mPowerManager.wakeUp(
time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
-
- // NOTE, the incoming view can sometimes be the entire container... unsure if
- // this location is valuable enough
- if (where != null) {
- where.getLocationInWindow(mTmpInt2);
- mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
- mTmpInt2[1] + where.getHeight() / 2);
- } else {
- mWakeUpTouchLocation = new PointF(-1, -1);
- }
mFalsingCollector.onScreenOnFromTouch();
}
}
@@ -1955,7 +1941,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
PowerManager.WAKE_REASON_APPLICATION,
"com.android.systemui:full_screen_intent");
mWakeUpComingFromTouch = false;
- mWakeUpTouchLocation = null;
}
}
@@ -3225,7 +3210,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
|| (mDozing && mDozeParameters.shouldControlScreenOff()
&& visibleNotOccludedOrWillBe);
- mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
+ mNotificationPanelViewController.setDozing(mDozing, animate);
updateQsExpansionEnabled();
Trace.endSection();
}
@@ -3556,7 +3541,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
mWakeUpComingFromTouch = false;
- mWakeUpTouchLocation = null;
updateVisibleToUser();
updateNotificationPanelTouchState();
@@ -4065,7 +4049,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
@Nullable ActivityLaunchAnimator.Controller animationController) {
final boolean willLaunchResolverActivity = intent.isActivity()
- && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
+ && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent,
mLockscreenUserManager.getCurrentUserId());
boolean animate = !willLaunchResolverActivity
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index fc8e7d5f6aa2..deb6150f773b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -225,6 +225,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
public void addDemoWifiView(WifiIconState state) {
Log.d(TAG, "addDemoWifiView: ");
+ // TODO(b/238425913): Migrate this view to {@code ModernStatusBarWifiView}.
StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot);
int viewIndex = getChildCount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 30b640b583e6..ed186ab6a10b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -40,6 +40,7 @@ import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.BaseStatusBarWifiView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
@@ -47,12 +48,16 @@ import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
+import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel;
import com.android.systemui.util.Assert;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
+import javax.inject.Provider;
public interface StatusBarIconController {
@@ -128,8 +133,12 @@ public interface StatusBarIconController {
private final DarkIconDispatcher mDarkIconDispatcher;
private int mIconHPadding;
- public DarkIconManager(LinearLayout linearLayout, FeatureFlags featureFlags) {
- super(linearLayout, featureFlags);
+ public DarkIconManager(
+ LinearLayout linearLayout,
+ FeatureFlags featureFlags,
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Provider<WifiViewModel> wifiViewModelProvider) {
+ super(linearLayout, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
@@ -183,14 +192,40 @@ public interface StatusBarIconController {
mDarkIconDispatcher.removeDarkReceiver(mDemoStatusIcons);
super.exitDemoMode();
}
+
+ @SysUISingleton
+ public static class Factory {
+ private final FeatureFlags mFeatureFlags;
+ private final StatusBarPipelineFlags mStatusBarPipelineFlags;
+ private final Provider<WifiViewModel> mWifiViewModelProvider;
+
+ @Inject
+ public Factory(
+ FeatureFlags featureFlags,
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Provider<WifiViewModel> wifiViewModelProvider) {
+ mFeatureFlags = featureFlags;
+ mStatusBarPipelineFlags = statusBarPipelineFlags;
+ mWifiViewModelProvider = wifiViewModelProvider;
+ }
+
+ public DarkIconManager create(LinearLayout group) {
+ return new DarkIconManager(
+ group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider);
+ }
+ }
}
/** */
class TintedIconManager extends IconManager {
private int mColor;
- public TintedIconManager(ViewGroup group, FeatureFlags featureFlags) {
- super(group, featureFlags);
+ public TintedIconManager(
+ ViewGroup group,
+ FeatureFlags featureFlags,
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Provider<WifiViewModel> wifiViewModelProvider) {
+ super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
}
@Override
@@ -223,14 +258,22 @@ public interface StatusBarIconController {
@SysUISingleton
public static class Factory {
private final FeatureFlags mFeatureFlags;
+ private final StatusBarPipelineFlags mStatusBarPipelineFlags;
+ private final Provider<WifiViewModel> mWifiViewModelProvider;
@Inject
- public Factory(FeatureFlags featureFlags) {
+ public Factory(
+ FeatureFlags featureFlags,
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Provider<WifiViewModel> wifiViewModelProvider) {
mFeatureFlags = featureFlags;
+ mStatusBarPipelineFlags = statusBarPipelineFlags;
+ mWifiViewModelProvider = wifiViewModelProvider;
}
public TintedIconManager create(ViewGroup group) {
- return new TintedIconManager(group, mFeatureFlags);
+ return new TintedIconManager(
+ group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider);
}
}
}
@@ -239,8 +282,10 @@ public interface StatusBarIconController {
* Turns info from StatusBarIconController into ImageViews in a ViewGroup.
*/
class IconManager implements DemoModeCommandReceiver {
- private final FeatureFlags mFeatureFlags;
protected final ViewGroup mGroup;
+ private final FeatureFlags mFeatureFlags;
+ private final StatusBarPipelineFlags mStatusBarPipelineFlags;
+ private final Provider<WifiViewModel> mWifiViewModelProvider;
protected final Context mContext;
protected final int mIconSize;
// Whether or not these icons show up in dumpsys
@@ -254,9 +299,15 @@ public interface StatusBarIconController {
protected ArrayList<String> mBlockList = new ArrayList<>();
- public IconManager(ViewGroup group, FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
+ public IconManager(
+ ViewGroup group,
+ FeatureFlags featureFlags,
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Provider<WifiViewModel> wifiViewModelProvider) {
mGroup = group;
+ mFeatureFlags = featureFlags;
+ mStatusBarPipelineFlags = statusBarPipelineFlags;
+ mWifiViewModelProvider = wifiViewModelProvider;
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_icon_size);
@@ -308,7 +359,7 @@ public interface StatusBarIconController {
return addIcon(index, slot, blocked, holder.getIcon());
case TYPE_WIFI:
- return addSignalIcon(index, slot, holder.getWifiState());
+ return addWifiIcon(index, slot, holder.getWifiState());
case TYPE_MOBILE:
return addMobileIcon(index, slot, holder.getMobileState());
@@ -327,9 +378,17 @@ public interface StatusBarIconController {
}
@VisibleForTesting
- protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
- StatusBarWifiView view = onCreateStatusBarWifiView(slot);
- view.applyWifiState(state);
+ protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) {
+ final BaseStatusBarWifiView view;
+ if (mStatusBarPipelineFlags.isNewPipelineFrontendEnabled()) {
+ view = onCreateModernStatusBarWifiView(slot);
+ // When [ModernStatusBarWifiView] is created, it will automatically apply the
+ // correct view state so we don't need to call applyWifiState.
+ } else {
+ StatusBarWifiView wifiView = onCreateStatusBarWifiView(slot);
+ wifiView.applyWifiState(state);
+ view = wifiView;
+ }
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
@@ -359,6 +418,11 @@ public interface StatusBarIconController {
return view;
}
+ private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) {
+ return ModernStatusBarWifiView.constructAndBind(
+ mContext, slot, mWifiViewModelProvider.get());
+ }
+
private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
return view;
@@ -415,9 +479,8 @@ public interface StatusBarIconController {
onSetIcon(viewIndex, holder.getIcon());
return;
case TYPE_WIFI:
- onSetSignalIcon(viewIndex, holder.getWifiState());
+ onSetWifiIcon(viewIndex, holder.getWifiState());
return;
-
case TYPE_MOBILE:
onSetMobileIcon(viewIndex, holder.getMobileState());
default:
@@ -425,10 +488,16 @@ public interface StatusBarIconController {
}
}
- public void onSetSignalIcon(int viewIndex, WifiIconState state) {
- StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex);
- if (wifiView != null) {
- wifiView.applyWifiState(state);
+ public void onSetWifiIcon(int viewIndex, WifiIconState state) {
+ View view = mGroup.getChildAt(viewIndex);
+ if (view instanceof StatusBarWifiView) {
+ ((StatusBarWifiView) view).applyWifiState(state);
+ } else if (view instanceof ModernStatusBarWifiView) {
+ // ModernStatusBarWifiView will automatically apply state based on its callbacks, so
+ // we don't need to call applyWifiState.
+ } else {
+ throw new IllegalStateException("View at " + viewIndex + " must be of type "
+ + "StatusBarWifiView or ModernStatusBarWifiView");
}
if (mIsInDemoMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 374f0916fb33..5cd2ba1b1cf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -223,12 +223,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
final boolean willLaunchResolverActivity = isActivityIntent
- && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
+ && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent,
mLockscreenUserManager.getCurrentUserId());
final boolean animate = !willLaunchResolverActivity
&& mCentralSurfaces.shouldAnimateLaunch(isActivityIntent);
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
- && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
+ && mActivityIntentHelper.wouldPendingShowOverLockscreen(intent,
mLockscreenUserManager.getCurrentUserId());
ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 40b9a152057a..70af77e1eb36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -259,8 +259,9 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
final boolean isActivity = pendingIntent.isActivity();
if (isActivity || appRequestedAuth) {
mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent);
- final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
- pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
+ final boolean afterKeyguardGone = mActivityIntentHelper
+ .wouldPendingLaunchResolverActivity(pendingIntent,
+ mLockscreenUserManager.getCurrentUserId());
mActivityStarter.dismissKeyguardThenExecute(() -> {
mActionClickLogger.logKeyguardGone(pendingIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 200f45f7f8a3..fb5b0960bb86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -286,6 +286,7 @@ public abstract class StatusBarViewModule {
PanelExpansionStateManager panelExpansionStateManager,
FeatureFlags featureFlags,
StatusBarIconController statusBarIconController,
+ StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController,
NotificationPanelViewController notificationPanelViewController,
@@ -306,6 +307,7 @@ public abstract class StatusBarViewModule {
panelExpansionStateManager,
featureFlags,
statusBarIconController,
+ darkIconManagerFactory,
statusBarHideIconsForBouncerManager,
keyguardStateController,
notificationPanelViewController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index f61b488a4fbb..5fd72b7a5247 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -124,6 +124,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final StatusBarIconController mStatusBarIconController;
private final CarrierConfigTracker mCarrierConfigTracker;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ private final StatusBarIconController.DarkIconManager.Factory mDarkIconManagerFactory;
private final SecureSettings mSecureSettings;
private final Executor mMainExecutor;
private final DumpManager mDumpManager;
@@ -172,6 +173,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
PanelExpansionStateManager panelExpansionStateManager,
FeatureFlags featureFlags,
StatusBarIconController statusBarIconController,
+ StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController,
NotificationPanelViewController notificationPanelViewController,
@@ -193,6 +195,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mFeatureFlags = featureFlags;
mStatusBarIconController = statusBarIconController;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
+ mDarkIconManagerFactory = darkIconManagerFactory;
mKeyguardStateController = keyguardStateController;
mNotificationPanelViewController = notificationPanelViewController;
mStatusBarStateController = statusBarStateController;
@@ -232,7 +235,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
}
- mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
+ mDarkIconManager = mDarkIconManagerFactory.create(view.findViewById(R.id.statusIcons));
mDarkIconManager.setShouldLog(true);
updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
deleted file mode 100644
index 780a02da3410..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline
-
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
-import kotlinx.coroutines.flow.StateFlow
-
-/**
- * Interface exposing a flow for raw connectivity information. Clients should collect on
- * [rawConnectivityInfoFlow] to get updates on connectivity information.
- *
- * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
- * and all clients get references to the same flow.
- *
- * This will be used for the new status bar pipeline to compile information we need to display some
- * of the icons in the RHS of the status bar.
- */
-interface ConnectivityInfoCollector {
- val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
-}
-
-/**
- * An object containing all of the raw connectivity information.
- *
- * Importantly, all the information in this object should not be processed at all (i.e., the data
- * that we receive from callbacks should be piped straight into this object and not be filtered,
- * manipulated, or processed in any way). Instead, any listeners on
- * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
- *
- * This allows us to keep all the processing in one place which is beneficial for logging and
- * debugging purposes.
- */
-data class RawConnectivityInfo(
- val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
-)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
deleted file mode 100644
index 99798f9b38d3..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilitiesRepo
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-
-/**
- * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
- * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
- */
-@SysUISingleton
-class ConnectivityInfoCollectorImpl @Inject constructor(
- networkCapabilitiesRepo: NetworkCapabilitiesRepo,
- @Application scope: CoroutineScope,
-) : ConnectivityInfoCollector {
- override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
- // TODO(b/238425913): Collect all the separate flows for individual raw information into
- // this final flow.
- networkCapabilitiesRepo.dataStream
- .map {
- RawConnectivityInfo(networkCapabilityInfo = it)
- }
- .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
index 64c47f679142..fe846747d0c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -20,31 +20,21 @@ import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/**
- * A processor that transforms raw connectivity information that we get from callbacks and turns it
- * into a list of displayable connectivity information.
+ * A temporary object that collects on [WifiViewModel] flows for debugging purposes.
*
- * This will be used for the new status bar pipeline to calculate the list of icons that should be
- * displayed in the RHS of the status bar.
- *
- * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
+ * This will eventually get migrated to a view binder that will use the flow outputs to set state on
+ * views. For now, this just collects on flows so that the information gets logged.
*/
@SysUISingleton
class ConnectivityInfoProcessor @Inject constructor(
- connectivityInfoCollector: ConnectivityInfoCollector,
context: Context,
// TODO(b/238425913): Don't use the application scope; instead, use the status bar view's
// scope so we only do work when there's UI that cares about it.
@@ -52,23 +42,8 @@ class ConnectivityInfoProcessor @Inject constructor(
private val statusBarPipelineFlags: StatusBarPipelineFlags,
private val wifiViewModelProvider: Provider<WifiViewModel>,
) : CoreStartable(context) {
- // Note: This flow will not start running until a client calls `collect` on it, which means that
- // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
- // happens.
- // TODO(b/238425913): Delete this.
- val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
- if (!statusBarPipelineFlags.isNewPipelineEnabled())
- emptyFlow()
- else connectivityInfoCollector.rawConnectivityInfoFlow
- .map { it.process() }
- .stateIn(
- scope,
- started = Lazily,
- initialValue = ProcessedConnectivityInfo()
- )
-
override fun start() {
- if (!statusBarPipelineFlags.isNewPipelineEnabled()) {
+ if (!statusBarPipelineFlags.isNewPipelineBackendEnabled()) {
return
}
// TODO(b/238425913): The view binder should do this instead. For now, do it here so we can
@@ -77,17 +52,4 @@ class ConnectivityInfoProcessor @Inject constructor(
wifiViewModelProvider.get().isActivityInVisible.collect { }
}
}
-
- private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
- // TODO(b/238425913): Actually process the raw info into meaningful data.
- return ProcessedConnectivityInfo(this.networkCapabilityInfo)
- }
}
-
-/**
- * An object containing connectivity info that has been processed into data that can be directly
- * used by the status bar (and potentially other SysUI areas) to display icons.
- */
-data class ProcessedConnectivityInfo(
- val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
-)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 589cdb8182cb..9b8b6434827e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -25,13 +25,28 @@ import javax.inject.Inject
@SysUISingleton
class StatusBarPipelineFlags @Inject constructor(private val featureFlags: FeatureFlags) {
/**
- * Returns true if we should run the new pipeline.
+ * Returns true if we should run the new pipeline backend.
*
- * TODO(b/238425913): We may want to split this out into:
- * (1) isNewPipelineLoggingEnabled(), where the new pipeline runs and logs its decisions but
- * doesn't change the UI at all.
- * (2) isNewPipelineEnabled(), where the new pipeline runs and does change the UI (and the old
- * pipeline doesn't change the UI).
+ * The new pipeline backend hooks up to all our external callbacks, logs those callback inputs,
+ * and logs the output state.
*/
- fun isNewPipelineEnabled(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_PIPELINE)
+ fun isNewPipelineBackendEnabled(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_STATUS_BAR_PIPELINE_BACKEND)
+
+ /**
+ * Returns true if we should run the new pipeline frontend *and* backend.
+ *
+ * The new pipeline frontend will use the outputted state from the new backend and will make the
+ * correct changes to the UI.
+ */
+ fun isNewPipelineFrontendEnabled(): Boolean =
+ isNewPipelineBackendEnabled() &&
+ featureFlags.isEnabled(Flags.NEW_STATUS_BAR_PIPELINE_FRONTEND)
+
+ /**
+ * Returns true if we should apply some coloring to icons that were rendered with the new
+ * pipeline to help with debugging.
+ */
+ // For now, just always apply the debug coloring if we've enabled frontend rendering.
+ fun useNewPipelineDebugColoring(): Boolean = isNewPipelineFrontendEnabled()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 7abe19e7bbe0..88eccb5ba47f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.pipeline.dagger
import com.android.systemui.CoreStartable
-import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
-import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
@@ -36,10 +34,5 @@ abstract class StatusBarPipelineModule {
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
@Binds
- abstract fun provideConnectivityInfoCollector(
- impl: ConnectivityInfoCollectorImpl
- ): ConnectivityInfoCollector
-
- @Binds
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
index a5fff5e65ebc..2a89309f7200 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
@@ -31,6 +31,9 @@ import kotlinx.coroutines.flow.onEach
class ConnectivityPipelineLogger @Inject constructor(
@StatusBarConnectivityLog private val buffer: LogBuffer,
) {
+ /**
+ * Logs a change in one of the **raw inputs** to the connectivity pipeline.
+ */
fun logInputChange(callbackName: String, changeInfo: String) {
buffer.log(
SB_LOGGING_TAG,
@@ -45,6 +48,41 @@ class ConnectivityPipelineLogger @Inject constructor(
)
}
+ /**
+ * Logs a **data transformation** that we performed within the connectivity pipeline.
+ */
+ fun logTransformation(transformationName: String, oldValue: Any?, newValue: Any?) {
+ if (oldValue == newValue) {
+ buffer.log(
+ SB_LOGGING_TAG,
+ LogLevel.INFO,
+ {
+ str1 = transformationName
+ str2 = oldValue.toString()
+ },
+ {
+ "Transform: $str1: $str2 (transformation didn't change it)"
+ }
+ )
+ } else {
+ buffer.log(
+ SB_LOGGING_TAG,
+ LogLevel.INFO,
+ {
+ str1 = transformationName
+ str2 = oldValue.toString()
+ str3 = newValue.toString()
+ },
+ {
+ "Transform: $str1: $str2 -> $str3"
+ }
+ )
+ }
+ }
+
+ /**
+ * Logs a change in one of the **outputs** to the connectivity pipeline.
+ */
fun logOutputChange(outputParamName: String, changeInfo: String) {
buffer.log(
SB_LOGGING_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
new file mode 100644
index 000000000000..5566fa6b5835
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.data.model
+
+/** Provides information about the current wifi network. */
+sealed class WifiNetworkModel {
+ /** A model representing that we have no active wifi network. */
+ object Inactive : WifiNetworkModel()
+
+ /** Provides information about an active wifi network. */
+ class Active(
+ /**
+ * The [android.net.Network.netId] we received from
+ * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
+ *
+ * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
+ */
+ val networkId: Int,
+
+ /** See [android.net.wifi.WifiInfo.ssid]. */
+ val ssid: String? = null,
+
+ /** See [android.net.wifi.WifiInfo.isPasspointAp]. */
+ val isPasspointAccessPoint: Boolean = false,
+
+ /** See [android.net.wifi.WifiInfo.isOsuAp]. */
+ val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
+
+ /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
+ val passpointProviderFriendlyName: String? = null,
+ ) : WifiNetworkModel()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepo.kt
deleted file mode 100644
index 6c0a44524e3a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepo.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.statusbar.pipeline.wifi.data.repository
-
-import android.annotation.SuppressLint
-import android.net.ConnectivityManager
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkRequest
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.stateIn
-
-/**
- * Repository that contains all relevant [NetworkCapabilities] for the current networks.
- *
- * TODO(b/238425913): Figure out how to merge this with [WifiRepository].
- */
-@SysUISingleton
-class NetworkCapabilitiesRepo @Inject constructor(
- connectivityManager: ConnectivityManager,
- @Application scope: CoroutineScope,
- logger: ConnectivityPipelineLogger,
-) {
- @SuppressLint("MissingPermission")
- val dataStream: StateFlow<Map<Int, NetworkCapabilityInfo>> = run {
- var state = emptyMap<Int, NetworkCapabilityInfo>()
- callbackFlow {
- val networkRequest: NetworkRequest =
- NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .build()
- val callback =
- // TODO (b/240569788): log these using [LogBuffer]
- object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
- override fun onCapabilitiesChanged(
- network: Network,
- networkCapabilities: NetworkCapabilities
- ) {
- logger.logOnCapabilitiesChanged(network, networkCapabilities)
- state =
- state.toMutableMap().also {
- it[network.getNetId()] =
- NetworkCapabilityInfo(network, networkCapabilities)
- }
- trySend(state)
- }
-
- override fun onLost(network: Network) {
- logger.logOnLost(network)
- state = state.toMutableMap().also { it.remove(network.getNetId()) }
- trySend(state)
- }
- }
- connectivityManager.registerNetworkCallback(networkRequest, callback)
-
- awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
- }
- .stateIn(scope, started = Lazily, initialValue = state)
- }
-}
-
-/** contains info about network capabilities. */
-data class NetworkCapabilityInfo(
- val network: Network,
- val capabilities: NetworkCapabilities,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index 012dde5fb8f2..43fbabca823f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -16,16 +16,26 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
+import android.annotation.SuppressLint
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.TrafficStateCallback
import android.util.Log
+import com.android.settingslib.Utils
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -38,9 +48,9 @@ import kotlinx.coroutines.flow.flowOf
*/
interface WifiRepository {
/**
- * Observable for the current state of wifi; `null` when there is no active wifi.
+ * Observable for the current wifi network.
*/
- val wifiModel: Flow<WifiModel?>
+ val wifiNetwork: Flow<WifiNetworkModel>
/**
* Observable for the current wifi network activity.
@@ -51,14 +61,57 @@ interface WifiRepository {
/** Real implementation of [WifiRepository]. */
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
+@SuppressLint("MissingPermission")
class WifiRepositoryImpl @Inject constructor(
+ connectivityManager: ConnectivityManager,
wifiManager: WifiManager?,
@Main mainExecutor: Executor,
logger: ConnectivityPipelineLogger,
) : WifiRepository {
+ override val wifiNetwork: Flow<WifiNetworkModel> = conflatedCallbackFlow {
+ var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
- // TODO(b/238425913): Actually implement the wifiModel flow.
- override val wifiModel: Flow<WifiModel?> = flowOf(WifiModel(ssid = "AB"))
+ val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ networkCapabilities: NetworkCapabilities
+ ) {
+ logger.logOnCapabilitiesChanged(network, networkCapabilities)
+
+ val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+ if (wifiInfo?.isPrimary == true) {
+ val wifiNetworkModel = wifiInfoToModel(wifiInfo, network.getNetId())
+ logger.logTransformation(
+ WIFI_NETWORK_CALLBACK_NAME,
+ oldValue = currentWifi,
+ newValue = wifiNetworkModel
+ )
+ currentWifi = wifiNetworkModel
+ trySend(wifiNetworkModel)
+ }
+ }
+
+ override fun onLost(network: Network) {
+ logger.logOnLost(network)
+ val wifi = currentWifi
+ if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
+ val newNetworkModel = WifiNetworkModel.Inactive
+ logger.logTransformation(
+ WIFI_NETWORK_CALLBACK_NAME,
+ oldValue = wifi,
+ newValue = newNetworkModel
+ )
+ currentWifi = newNetworkModel
+ trySend(newNetworkModel)
+ }
+ }
+ }
+
+ trySend(WIFI_NETWORK_DEFAULT)
+ connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
+
+ awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+ }
override val wifiActivity: Flow<WifiActivityModel> =
if (wifiManager == null) {
@@ -71,8 +124,8 @@ class WifiRepositoryImpl @Inject constructor(
trySend(trafficStateToWifiActivityModel(state))
}
- wifiManager.registerTrafficStateCallback(mainExecutor, callback)
trySend(ACTIVITY_DEFAULT)
+ wifiManager.registerTrafficStateCallback(mainExecutor, callback)
awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
}
@@ -80,6 +133,13 @@ class WifiRepositoryImpl @Inject constructor(
companion object {
val ACTIVITY_DEFAULT = WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+ // Start out with no known wifi network.
+ // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
+ // initial fetch to get a starting wifi network. But, it uses a deprecated API
+ // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
+ // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
+ // NetworkCallback inside [wifiNetwork] for our wifi network information.
+ val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
private fun trafficStateToWifiActivityModel(state: Int): WifiActivityModel {
return WifiActivityModel(
@@ -90,6 +150,30 @@ class WifiRepositoryImpl @Inject constructor(
)
}
+ private fun networkCapabilitiesToWifiInfo(
+ networkCapabilities: NetworkCapabilities
+ ): WifiInfo? {
+ return when {
+ networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
+ networkCapabilities.transportInfo as WifiInfo?
+ networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
+ // Sometimes, cellular networks can act as wifi networks (known as VCN --
+ // virtual carrier network). So, see if this cellular network has wifi info.
+ Utils.tryGetWifiInfoForVcn(networkCapabilities)
+ else -> null
+ }
+ }
+
+ private fun wifiInfoToModel(wifiInfo: WifiInfo, networkId: Int): WifiNetworkModel {
+ return WifiNetworkModel.Active(
+ networkId,
+ wifiInfo.ssid,
+ wifiInfo.isPasspointAp,
+ wifiInfo.isOsuAp,
+ wifiInfo.passpointProviderFriendlyName
+ )
+ }
+
private fun prettyPrintActivity(activity: Int): String {
return when (activity) {
TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
@@ -99,5 +183,15 @@ class WifiRepositoryImpl @Inject constructor(
else -> "INVALID"
}
}
+
+ private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
+ NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build()
+
+ private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index f705399af85d..a9da63b43aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
import android.net.wifi.WifiManager
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -34,13 +35,15 @@ import kotlinx.coroutines.flow.map
class WifiInteractor @Inject constructor(
repository: WifiRepository,
) {
- private val ssid: Flow<String?> = repository.wifiModel.map { info ->
- when {
- info == null -> null
- info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
- info.passpointProviderFriendlyName
- info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
- else -> null
+ private val ssid: Flow<String?> = repository.wifiNetwork.map { info ->
+ when (info) {
+ is WifiNetworkModel.Inactive -> null
+ is WifiNetworkModel.Active -> when {
+ info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
+ info.passpointProviderFriendlyName
+ info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
+ else -> null
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
new file mode 100644
index 000000000000..b06aaf441f5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.ui.binder
+
+import android.content.res.ColorStateList
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/**
+ * Binds a wifi icon in the status bar to its view-model.
+ *
+ * To use this properly, users should maintain a one-to-one relationship between the [View] and the
+ * view-binding, binding each view only once. It is okay and expected for the same instance of the
+ * view-model to be reused for multiple view/view-binder bindings.
+ */
+@OptIn(InternalCoroutinesApi::class)
+object WifiViewBinder {
+ /** Binds the view to the view-model, continuing to update the former based on the latter. */
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: WifiViewModel,
+ ) {
+ val iconView = view.requireViewById<ImageView>(R.id.wifi_signal)
+
+ view.isVisible = true
+ iconView.isVisible = true
+ iconView.setImageDrawable(view.context.getDrawable(WIFI_FULL_ICONS[2]))
+
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.tint.collect { tint ->
+ iconView.imageTintList = ColorStateList.valueOf(tint)
+ }
+ }
+ }
+ }
+
+ // TODO(b/238425913): Hook up to [viewModel] to render actual changes to the wifi icon.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
new file mode 100644
index 000000000000..c14a897fffab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.ui.view
+
+import android.content.Context
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import com.android.systemui.R
+import com.android.systemui.statusbar.BaseStatusBarWifiView
+import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
+import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
+
+/**
+ * A new and more modern implementation of [com.android.systemui.statusbar.StatusBarWifiView] that
+ * is updated by [WifiViewBinder].
+ */
+class ModernStatusBarWifiView(
+ context: Context,
+ attrs: AttributeSet?
+) : BaseStatusBarWifiView(context, attrs) {
+
+ private lateinit var slot: String
+
+ override fun onDarkChanged(areas: ArrayList<Rect>?, darkIntensity: Float, tint: Int) {
+ // TODO(b/238425913)
+ }
+
+ override fun getSlot() = slot
+
+ override fun setStaticDrawableColor(color: Int) {
+ // TODO(b/238425913)
+ }
+
+ override fun setDecorColor(color: Int) {
+ // TODO(b/238425913)
+ }
+
+ override fun setVisibleState(state: Int, animate: Boolean) {
+ // TODO(b/238425913)
+ }
+
+ override fun getVisibleState(): Int {
+ // TODO(b/238425913)
+ return STATE_ICON
+ }
+
+ override fun isIconVisible(): Boolean {
+ // TODO(b/238425913)
+ return true
+ }
+
+ /** Set the slot name for this view. */
+ private fun setSlot(slotName: String) {
+ this.slot = slotName
+ }
+
+ companion object {
+ /**
+ * Inflates a new instance of [ModernStatusBarWifiView], binds it to [viewModel], and
+ * returns it.
+ */
+ @JvmStatic
+ fun constructAndBind(
+ context: Context,
+ slot: String,
+ viewModel: WifiViewModel,
+ ): ModernStatusBarWifiView {
+ return (
+ LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null)
+ as ModernStatusBarWifiView
+ ).also {
+ it.setSlot(slot)
+ WifiViewBinder.bind(it, viewModel)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index b990eb7d9173..7a262605681d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
+import android.graphics.Color
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
/**
@@ -30,9 +33,10 @@ import kotlinx.coroutines.flow.flowOf
* TODO(b/238425913): Hook this up to the real status bar wifi view using a view binder.
*/
class WifiViewModel @Inject constructor(
- private val constants: WifiConstants,
- private val logger: ConnectivityPipelineLogger,
- private val interactor: WifiInteractor,
+ statusBarPipelineFlags: StatusBarPipelineFlags,
+ private val constants: WifiConstants,
+ private val logger: ConnectivityPipelineLogger,
+ private val interactor: WifiInteractor,
) {
val isActivityInVisible: Flow<Boolean>
get() =
@@ -42,4 +46,11 @@ class WifiViewModel @Inject constructor(
interactor.hasActivityIn
}
.logOutputChange(logger, "activityInVisible")
+
+ /** The tint that should be applied to the icon. */
+ val tint: Flow<Int> = if (!statusBarPipelineFlags.useNewPipelineDebugColoring()) {
+ emptyFlow()
+ } else {
+ flowOf(Color.CYAN)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index dfcdaefd8e03..836d57131fac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -45,7 +45,6 @@ import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -287,10 +286,6 @@ public class UserSwitcherController implements Dumpable {
refreshUsers(UserHandle.USER_NULL);
}
- private static boolean isEnableGuestModeUxChanges(Context context) {
- return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES);
- }
-
/**
* Refreshes users from UserManager.
*
@@ -549,17 +544,9 @@ public class UserSwitcherController implements Dumpable {
}
if (currUserInfo != null && currUserInfo.isGuest()) {
- if (isEnableGuestModeUxChanges(mContext)) {
- showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
- record.resolveId(), dialogShower);
- return;
- } else {
- if (currUserInfo.isEphemeral()) {
- showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
- record.resolveId(), dialogShower);
- return;
- }
- }
+ showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
+ record.resolveId(), dialogShower);
+ return;
}
if (dialogShower != null) {
@@ -1056,14 +1043,8 @@ public class UserSwitcherController implements Dumpable {
public String getName(Context context, UserRecord item) {
if (item.isGuest) {
if (item.isCurrent) {
- if (isEnableGuestModeUxChanges(context)) {
- return context.getString(
- com.android.settingslib.R.string.guest_exit_quick_settings_button);
- } else {
- return context.getString(mController.mGuestUserAutoCreated
- ? com.android.settingslib.R.string.guest_reset_guest
- : com.android.settingslib.R.string.guest_exit_guest);
- }
+ return context.getString(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button);
} else {
if (item.info != null) {
return context.getString(com.android.internal.R.string.guest_name);
@@ -1080,13 +1061,8 @@ public class UserSwitcherController implements Dumpable {
? com.android.settingslib.R.string.guest_resetting
: com.android.internal.R.string.guest_name);
} else {
- if (isEnableGuestModeUxChanges(context)) {
- // we always show "guest" as string, instead of "add guest"
- return context.getString(com.android.internal.R.string.guest_name);
- } else {
- return context.getString(
- com.android.settingslib.R.string.guest_new_guest);
- }
+ // we always show "guest" as string, instead of "add guest"
+ return context.getString(com.android.internal.R.string.guest_name);
}
}
}
@@ -1108,11 +1084,7 @@ public class UserSwitcherController implements Dumpable {
protected static Drawable getIconDrawable(Context context, UserRecord item) {
int iconRes;
if (item.isAddUser) {
- if (isEnableGuestModeUxChanges(context)) {
- iconRes = R.drawable.ic_add;
- } else {
- iconRes = R.drawable.ic_account_circle_filled;
- }
+ iconRes = R.drawable.ic_add;
} else if (item.isGuest) {
iconRes = R.drawable.ic_account_circle;
} else if (item.isAddSupervisedUser) {
@@ -1289,46 +1261,32 @@ public class UserSwitcherController implements Dumpable {
ExitGuestDialog(Context context, int guestId, boolean isGuestEphemeral,
int targetId) {
super(context);
- if (isEnableGuestModeUxChanges(context)) {
- if (isGuestEphemeral) {
- setTitle(context.getString(
- com.android.settingslib.R.string.guest_exit_dialog_title));
- setMessage(context.getString(
- com.android.settingslib.R.string.guest_exit_dialog_message));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(
- com.android.settingslib.R.string.guest_exit_dialog_button), this);
- } else {
- setTitle(context.getString(
- com.android.settingslib
- .R.string.guest_exit_dialog_title_non_ephemeral));
- setMessage(context.getString(
- com.android.settingslib
- .R.string.guest_exit_dialog_message_non_ephemeral));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_NEGATIVE,
- context.getString(
- com.android.settingslib.R.string.guest_exit_clear_data_button),
- this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(
- com.android.settingslib.R.string.guest_exit_save_data_button),
- this);
- }
+ if (isGuestEphemeral) {
+ setTitle(context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_title));
+ setMessage(context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_message));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_button), this);
} else {
- setTitle(mGuestUserAutoCreated
- ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
- : com.android.settingslib.R.string.guest_remove_guest_dialog_title);
- setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
+ setTitle(context.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_title_non_ephemeral));
+ setMessage(context.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_message_non_ephemeral));
setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_clear_data_button),
+ this);
setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(mGuestUserAutoCreated
- ? com.android.settingslib.R.string.guest_reset_guest_confirm_button
- : com.android.settingslib.R.string.guest_remove_guest_confirm_button),
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_save_data_button),
this);
}
SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
@@ -1345,39 +1303,29 @@ public class UserSwitcherController implements Dumpable {
if (mFalsingManager.isFalseTap(penalty)) {
return;
}
- if (isEnableGuestModeUxChanges(getContext())) {
- if (mIsGuestEphemeral) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- mDialogLaunchAnimator.dismissStack(this);
- // Ephemeral guest: exit guest, guest is removed by the system
- // on exit, since its marked ephemeral
- exitGuestUser(mGuestId, mTargetId, false);
- } else if (which == DialogInterface.BUTTON_NEGATIVE) {
- // Cancel clicked, do nothing
- cancel();
- }
- } else {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- mDialogLaunchAnimator.dismissStack(this);
- // Non-ephemeral guest: exit guest, guest is not removed by the system
- // on exit, since its marked non-ephemeral
- exitGuestUser(mGuestId, mTargetId, false);
- } else if (which == DialogInterface.BUTTON_NEGATIVE) {
- mDialogLaunchAnimator.dismissStack(this);
- // Non-ephemeral guest: remove guest and then exit
- exitGuestUser(mGuestId, mTargetId, true);
- } else if (which == DialogInterface.BUTTON_NEUTRAL) {
- // Cancel clicked, do nothing
- cancel();
- }
+ if (mIsGuestEphemeral) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDialogLaunchAnimator.dismissStack(this);
+ // Ephemeral guest: exit guest, guest is removed by the system
+ // on exit, since its marked ephemeral
+ exitGuestUser(mGuestId, mTargetId, false);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ // Cancel clicked, do nothing
+ cancel();
}
} else {
- if (which == BUTTON_NEUTRAL) {
- cancel();
- } else {
- mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDialogLaunchAnimator.dismissStack(this);
+ // Non-ephemeral guest: exit guest, guest is not removed by the system
+ // on exit, since its marked non-ephemeral
+ exitGuestUser(mGuestId, mTargetId, false);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
mDialogLaunchAnimator.dismissStack(this);
- removeGuestUser(mGuestId, mTargetId);
+ // Non-ephemeral guest: remove guest and then exit
+ exitGuestUser(mGuestId, mTargetId, true);
+ } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ // Cancel clicked, do nothing
+ cancel();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt
new file mode 100644
index 000000000000..c0331e6000bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.runBlocking
+
+/**
+ * A utility for handling incoming IPCs from a Binder interface in the order that they are received.
+ *
+ * This class serves as a replacement for the common [android.os.Handler] message-queue pattern,
+ * where IPCs can arrive on arbitrary threads and are all enqueued onto a queue and processed by the
+ * Handler in-order.
+ *
+ * class MyService : Service() {
+ *
+ * private val serializer = IpcSerializer()
+ *
+ * // Need to invoke process() in order to actually process IPCs sent over the serializer.
+ * override fun onStart(...) = lifecycleScope.launch {
+ * serializer.process()
+ * }
+ *
+ * // In your binder implementation, use runSerializedBlocking to enqueue a function onto
+ * // the serializer.
+ * override fun onBind(intent: Intent?) = object : IAidlService.Stub() {
+ * override fun ipcMethodFoo() = serializer.runSerializedBlocking {
+ * ...
+ * }
+ *
+ * override fun ipcMethodBar() = serializer.runSerializedBlocking {
+ * ...
+ * }
+ * }
+ * }
+ */
+class IpcSerializer {
+
+ private val channel = Channel<Pair<CompletableDeferred<Unit>, Job>>()
+
+ /**
+ * Runs functions enqueued via usage of [runSerialized] and [runSerializedBlocking] serially.
+ * This method will never complete normally, so it must be launched in its own coroutine; if
+ * this is not actively running, no enqueued functions will be evaluated.
+ */
+ suspend fun process(): Nothing {
+ for ((start, finish) in channel) {
+ // Signal to the sender that serializer has reached this message
+ start.complete(Unit)
+ // Wait to hear from the sender that it has finished running it's work, before handling
+ // the next message
+ finish.join()
+ }
+ error("Unexpected end of serialization channel")
+ }
+
+ /**
+ * Enqueues [block] for evaluation by the serializer, suspending the caller until it has
+ * completed. It is up to the caller to define what thread this is evaluated in, determined
+ * by the [kotlin.coroutines.CoroutineContext] used.
+ */
+ suspend fun <R> runSerialized(block: suspend () -> R): R {
+ val start = CompletableDeferred(Unit)
+ val finish = CompletableDeferred(Unit)
+ // Enqueue our message on the channel.
+ channel.send(start to finish)
+ // Wait for the serializer to reach our message
+ start.await()
+ // Now evaluate the block
+ val result = block()
+ // Notify the serializer that we've completed evaluation
+ finish.complete(Unit)
+ return result
+ }
+
+ /**
+ * Enqueues [block] for evaluation by the serializer, blocking the binder thread until it has
+ * completed. Evaluation occurs on the binder thread, so methods like
+ * [android.os.Binder.getCallingUid] that depend on the current thread will work as expected.
+ */
+ fun <R> runSerializedBlocking(block: suspend () -> R): R = runBlocking { runSerialized(block) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index aeab2dff9421..199048ec7b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -50,9 +50,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -77,7 +75,6 @@ import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleEntry;
import com.android.wm.shell.bubbles.Bubbles;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -92,7 +89,7 @@ import java.util.function.IntConsumer;
* The SysUi side bubbles manager which communicate with other SysUi components.
*/
@SysUISingleton
-public class BubblesManager implements Dumpable {
+public class BubblesManager {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesManager" : TAG_BUBBLES;
@@ -137,7 +134,6 @@ public class BubblesManager implements Dumpable {
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
- DumpManager dumpManager,
Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context,
@@ -156,7 +152,6 @@ public class BubblesManager implements Dumpable {
notifCollection,
notifPipeline,
sysUiState,
- dumpManager,
sysuiMainExecutor);
} else {
return null;
@@ -180,7 +175,6 @@ public class BubblesManager implements Dumpable {
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
- DumpManager dumpManager,
Executor sysuiMainExecutor) {
mContext = context;
mBubbles = bubbles;
@@ -203,8 +197,6 @@ public class BubblesManager implements Dumpable {
setupNotifPipeline();
- dumpManager.registerDumpable(TAG, this);
-
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
@@ -648,11 +640,6 @@ public class BubblesManager implements Dumpable {
}
}
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- mBubbles.dump(pw, args);
- }
-
/** Checks whether bubbles are enabled for this user, handles negative userIds. */
public static boolean areBubblesEnabled(@NonNull Context context, @NonNull UserHandle user) {
if (user.getIdentifier() < 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index eba279587629..a4a59fc9d4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -29,14 +29,16 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.view.KeyEvent;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -47,11 +49,11 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.nano.WmShellTraceProto;
@@ -66,6 +68,7 @@ import com.android.wm.shell.sysui.ShellInterface;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -115,7 +118,7 @@ public final class WMShell extends CoreStartable
private final SysUiState mSysUiState;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final ProtoTracer mProtoTracer;
- private final UserInfoController mUserInfoController;
+ private final UserTracker mUserTracker;
private final Executor mSysUiMainExecutor;
// Listeners and callbacks. Note that we prefer member variable over anonymous class here to
@@ -144,9 +147,20 @@ public final class WMShell extends CoreStartable
mShell.onKeyguardDismissAnimationFinished();
}
};
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mShell.onUserChanged(newUser, userContext);
+ }
+
+ @Override
+ public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
+ mShell.onUserProfilesChanged(profiles);
+ }
+ };
private boolean mIsSysUiStateValid;
- private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
@@ -163,7 +177,7 @@ public final class WMShell extends CoreStartable
SysUiState sysUiState,
ProtoTracer protoTracer,
WakefulnessLifecycle wakefulnessLifecycle,
- UserInfoController userInfoController,
+ UserTracker userTracker,
@Main Executor sysUiMainExecutor) {
super(context);
mShell = shell;
@@ -178,7 +192,7 @@ public final class WMShell extends CoreStartable
mOneHandedOptional = oneHandedOptional;
mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
- mUserInfoController = userInfoController;
+ mUserTracker = userTracker;
mSysUiMainExecutor = sysUiMainExecutor;
}
@@ -192,8 +206,9 @@ public final class WMShell extends CoreStartable
mKeyguardStateController.addCallback(mKeyguardStateCallback);
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- // TODO: Consider piping config change and other common calls to a shell component to
- // delegate internally
+ // Subscribe to user changes
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
+
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
@@ -214,10 +229,6 @@ public final class WMShell extends CoreStartable
mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
});
-
- // The media session listener needs to be re-registered when switching users
- mUserInfoController.addCallback((String name, Drawable picture, String userAccount) ->
- pip.registerSessionListenerForCurrentUser());
}
@VisibleForTesting
@@ -267,15 +278,6 @@ public final class WMShell extends CoreStartable
}
});
- // TODO: Either move into ShellInterface or register a receiver on the Shell side directly
- mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onUserSwitchComplete(int userId) {
- oneHanded.onUserSwitch(userId);
- }
- };
- mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback);
-
mWakefulnessObserver =
new WakefulnessLifecycle.Observer() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index c281965550e4..3e00aec8cb28 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -1006,7 +1007,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN udfps is now enrolled
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
- callback.onEnrollmentsChanged();
+ callback.onEnrollmentsChanged(TYPE_FINGERPRINT);
// THEN isUdfspEnrolled is TRUE
assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index d158892e4ec5..e0d1f7a19130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -60,6 +60,9 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -154,7 +157,9 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Captor
- ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
+ ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
+ @Captor
+ ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
@Captor
ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
@Captor
@@ -193,25 +198,38 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point());
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
-
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */, "" /* softwareVersion */));
- componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
- "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
- "vendor/version/revision" /* softwareVersion */));
-
- FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
- 1 /* sensorId */,
- SensorProperties.STRENGTH_STRONG,
- 1 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequireHardwareAuthToken */);
- List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
- props.add(prop);
- when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ final List<ComponentInfoInternal> fpComponentInfo = List.of(
+ new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ final List<ComponentInfoInternal> faceComponentInfo = List.of(
+ new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
+ final List<FingerprintSensorPropertiesInternal> fpProps = List.of(
+ new FingerprintSensorPropertiesInternal(
+ 1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ fpComponentInfo,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequireHardwareAuthToken */));
+ when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(fpProps);
+
+ final List<FaceSensorPropertiesInternal> faceProps = List.of(
+ new FaceSensorPropertiesInternal(
+ 2 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ fpComponentInfo,
+ FaceSensorProperties.TYPE_RGB,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ true /* resetLockoutRequireHardwareAuthToken */));
+ when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps);
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
@@ -219,12 +237,15 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
- mAuthenticatorsRegisteredCaptor.capture());
+ mFpAuthenticatorsRegisteredCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorsRegisteredCaptor.capture());
when(mStatusBarStateController.isDozing()).thenReturn(false);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
- mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps);
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
// Ensures that the operations posted on the handler get executed.
mTestableLooper.processAllMessages();
@@ -237,6 +258,7 @@ public class AuthControllerTest extends SysuiTestCase {
throws RemoteException {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
+ reset(mFaceManager);
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
@@ -246,21 +268,27 @@ public class AuthControllerTest extends SysuiTestCase {
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
- mAuthenticatorsRegisteredCaptor.capture());
+ mFpAuthenticatorsRegisteredCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorsRegisteredCaptor.capture());
mTestableLooper.processAllMessages();
verify(mFingerprintManager, never()).registerBiometricStateListener(any());
+ verify(mFaceManager, never()).registerBiometricStateListener(any());
- mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
mTestableLooper.processAllMessages();
verify(mFingerprintManager).registerBiometricStateListener(any());
+ verify(mFaceManager).registerBiometricStateListener(any());
}
@Test
public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
+ reset(mFaceManager);
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
@@ -270,18 +298,25 @@ public class AuthControllerTest extends SysuiTestCase {
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
- mAuthenticatorsRegisteredCaptor.capture());
+ mFpAuthenticatorsRegisteredCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorsRegisteredCaptor.capture());
// Emulates a device with no authenticators (empty list).
- mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
mTestableLooper.processAllMessages();
verify(mFingerprintManager).registerBiometricStateListener(
mBiometricStateCaptor.capture());
+ verify(mFaceManager).registerBiometricStateListener(
+ mBiometricStateCaptor.capture());
// Enrollments changed for an unknown sensor.
- mBiometricStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */,
- 0xbeef /* sensorId */, true /* hasEnrollments */);
+ for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) {
+ listener.onEnrollmentsChanged(0 /* userId */,
+ 0xbeef /* sensorId */, true /* hasEnrollments */);
+ }
mTestableLooper.processAllMessages();
// Nothing should crash.
@@ -827,4 +862,3 @@ public class AuthControllerTest extends SysuiTestCase {
}
}
}
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 9ffc5a57cef6..b33f9a7f3933 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.doze;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP;
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
@@ -412,7 +414,7 @@ public class DozeSensorsTest extends SysuiTestCase {
// WHEN enrollment changes to TRUE
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
- mAuthControllerCallback.onEnrollmentsChanged();
+ mAuthControllerCallback.onEnrollmentsChanged(TYPE_FINGERPRINT);
// THEN mConfigured = TRUE
assertTrue(triggerSensor.mConfigured);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 01309f86a137..7f6b79b48939 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -308,6 +308,12 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
+ public void testOnViewDetachedRemovesViews() {
+ mController.onViewDetached();
+ verify(mView).removeAllStatusBarItemViews();
+ }
+
+ @Test
public void testWifiIconHiddenWhenWifiBecomesAvailable() {
// Make sure wifi starts out unavailable when onViewAttached is called, and then returns
// true on the second query.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index 09976e0e6192..571dd3d1faf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -106,7 +106,7 @@ public class ComplicationTypesUpdaterTest extends SysuiTestCase {
private ContentObserver captureSettingsObserver() {
verify(mSecureSettings).registerContentObserverForUser(
- eq(Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS),
+ eq(Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED),
mSettingsObserverCaptor.capture(), eq(UserHandle.myUserId()));
return mSettingsObserverCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 24d051508fde..5ec6bdf3c00b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
@@ -219,7 +221,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
Pair<Float, PointF> udfps = setupUdfps();
// WHEN all authenticators are registered
- mAuthControllerCallback.onAllAuthenticatorsRegistered();
+ mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
mDelayableExecutor.runAllReady();
// THEN lock icon view location is updated with the same coordinates as auth controller vals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 19491f41a0c1..14b85b8b5e56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -37,6 +37,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import kotlin.math.max
+import kotlin.math.min
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -127,6 +129,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
val testConfig =
TestConfig(
isVisible = true,
+ isClickable = true,
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
@@ -154,6 +157,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
val config =
TestConfig(
isVisible = true,
+ isClickable = true,
icon = mock(),
canShowWhileLocked = false,
intent = null, // This will cause it to tell the system that the click was handled.
@@ -201,6 +205,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
val testConfig =
TestConfig(
isVisible = true,
+ isClickable = true,
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
@@ -260,6 +265,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
testConfig =
TestConfig(
isVisible = true,
+ isClickable = true,
icon = mock(),
canShowWhileLocked = true,
)
@@ -269,6 +275,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
testConfig =
TestConfig(
isVisible = true,
+ isClickable = true,
icon = mock(),
canShowWhileLocked = false,
)
@@ -342,6 +349,129 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `isClickable - true when alpha at threshold`() = runBlockingTest {
+ repository.setKeyguardShowing(true)
+ repository.setBottomAreaAlpha(
+ KeyguardBottomAreaViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD
+ )
+
+ val testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ icon = mock(),
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = testConfig,
+ )
+
+ var latest: KeyguardQuickAffordanceViewModel? = null
+ val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig = testConfig,
+ configKey = configKey,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `isClickable - true when alpha above threshold`() = runBlockingTest {
+ repository.setKeyguardShowing(true)
+ var latest: KeyguardQuickAffordanceViewModel? = null
+ val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+ repository.setBottomAreaAlpha(
+ min(1f, KeyguardBottomAreaViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD + 0.1f),
+ )
+
+ val testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = true,
+ icon = mock(),
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = testConfig,
+ )
+
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig = testConfig,
+ configKey = configKey,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `isClickable - false when alpha below threshold`() = runBlockingTest {
+ repository.setKeyguardShowing(true)
+ var latest: KeyguardQuickAffordanceViewModel? = null
+ val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+ repository.setBottomAreaAlpha(
+ max(0f, KeyguardBottomAreaViewModel.AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD - 0.1f),
+ )
+
+ val testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = false,
+ icon = mock(),
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = testConfig,
+ )
+
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig = testConfig,
+ configKey = configKey,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `isClickable - false when alpha at zero`() = runBlockingTest {
+ repository.setKeyguardShowing(true)
+ var latest: KeyguardQuickAffordanceViewModel? = null
+ val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+ repository.setBottomAreaAlpha(0f)
+
+ val testConfig =
+ TestConfig(
+ isVisible = true,
+ isClickable = false,
+ icon = mock(),
+ canShowWhileLocked = false,
+ intent = Intent("action"),
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = testConfig,
+ )
+
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig = testConfig,
+ configKey = configKey,
+ )
+ job.cancel()
+ }
+
private suspend fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
repository.setDozeAmount(dozeAmount)
return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
@@ -384,6 +514,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
) {
checkNotNull(viewModel)
assertThat(viewModel.isVisible).isEqualTo(testConfig.isVisible)
+ assertThat(viewModel.isClickable).isEqualTo(testConfig.isClickable)
if (testConfig.isVisible) {
assertThat(viewModel.icon).isEqualTo(testConfig.icon)
viewModel.onClicked.invoke(
@@ -404,6 +535,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
private data class TestConfig(
val isVisible: Boolean,
+ val isClickable: Boolean = false,
val icon: ContainedDrawable? = null,
val canShowWhileLocked: Boolean = false,
val intent: Intent? = null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
new file mode 100644
index 000000000000..373af5cdf4b7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.lifecycle
+
+import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.arch.core.executor.TaskExecutor
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+/**
+ * Test rule that makes ArchTaskExecutor main thread assertions pass. There is one such assert
+ * in LifecycleRegistry.
+ */
+class InstantTaskExecutorRule : TestWatcher() {
+ // TODO(b/240620122): This is a copy of
+ // androidx/arch/core/executor/testing/InstantTaskExecutorRule which should be replaced
+ // with a dependency on the real library once b/ is cleared.
+ override fun starting(description: Description) {
+ super.starting(description)
+ ArchTaskExecutor.getInstance()
+ .setDelegate(
+ object : TaskExecutor() {
+ override fun executeOnDiskIO(runnable: Runnable) {
+ runnable.run()
+ }
+
+ override fun postToMainThread(runnable: Runnable) {
+ runnable.run()
+ }
+
+ override fun isMainThread(): Boolean {
+ return true
+ }
+ }
+ )
+ }
+
+ override fun finished(description: Description) {
+ super.finished(description)
+ ArchTaskExecutor.getInstance().setDelegate(null)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index 80f3e46b848f..91a6de6ae4c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -20,8 +20,6 @@ package com.android.systemui.lifecycle
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.view.ViewTreeObserver
-import androidx.arch.core.executor.ArchTaskExecutor
-import androidx.arch.core.executor.TaskExecutor
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
@@ -35,8 +33,6 @@ import kotlinx.coroutines.test.runBlockingTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.TestWatcher
-import org.junit.runner.Description
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mock
@@ -282,38 +278,4 @@ class RepeatWhenAttachedTest : SysuiTestCase() {
_invocations.add(Invocation(lifecycleOwner))
}
}
-
- /**
- * Test rule that makes ArchTaskExecutor main thread assertions pass. There is one such assert
- * in LifecycleRegistry.
- */
- class InstantTaskExecutorRule : TestWatcher() {
- // TODO(b/240620122): This is a copy of
- // androidx/arch/core/executor/testing/InstantTaskExecutorRule which should be replaced
- // with a dependency on the real library once b/ is cleared.
- override fun starting(description: Description) {
- super.starting(description)
- ArchTaskExecutor.getInstance()
- .setDelegate(
- object : TaskExecutor() {
- override fun executeOnDiskIO(runnable: Runnable) {
- runnable.run()
- }
-
- override fun postToMainThread(runnable: Runnable) {
- runnable.run()
- }
-
- override fun isMainThread(): Boolean {
- return true
- }
- }
- )
- }
-
- override fun finished(description: Description) {
- super.finished(description)
- ArchTaskExecutor.getInstance().setDelegate(null)
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 260bb8760f1c..22ecb4b93743 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -78,7 +78,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
- when(mMediaOutputController.isTransferring()).thenReturn(false);
+ when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false);
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
@@ -208,7 +208,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
@Test
public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() {
- when(mMediaOutputController.isTransferring()).thenReturn(true);
+ when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true);
when(mMediaDevice1.getState()).thenReturn(
LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
@@ -224,7 +224,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
@Test
public void onBindViewHolder_inTransferring_bindNonTransferringDevice_verifyView() {
- when(mMediaOutputController.isTransferring()).thenReturn(true);
+ when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true);
when(mMediaDevice2.getState()).thenReturn(
LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index be14cc51ef96..cb4f08e6c552 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -93,6 +93,10 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var insetsProvider: StatusBarContentInsetsProvider
+ @Mock
+ private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
+ @Mock
+ private lateinit var iconManager: StatusBarIconController.TintedIconManager
private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
@@ -106,6 +110,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
`when`(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
+ `when`(iconManagerFactory.create(any())).thenReturn(iconManager)
`when`(view.resources).thenReturn(mContext.resources)
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
@@ -122,7 +127,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
featureFlags,
variableDateViewControllerFactory,
batteryMeterViewController,
- insetsProvider
+ insetsProvider,
+ iconManagerFactory,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
new file mode 100644
index 000000000000..d91baa5e7fcb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class InternetTileTest extends SysuiTestCase {
+
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private NetworkController mNetworkController;
+ @Mock
+ private AccessPointController mAccessPointController;
+ @Mock
+ private InternetDialogFactory mInternetDialogFactory;
+
+ private TestableLooper mTestableLooper;
+ private InternetTile mTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+ when(mHost.getContext()).thenReturn(mContext);
+ when(mHost.getUserContext()).thenReturn(mContext);
+
+ mTile = new InternetTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mock(MetricsLogger.class),
+ mock(StatusBarStateController.class),
+ mock(ActivityStarter.class),
+ mock(QSLogger.class),
+ mNetworkController,
+ mAccessPointController,
+ mInternetDialogFactory
+ );
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void setConnectivityStatus_defaultNetworkNotExists_updateTile() {
+ mTile.mSignalCallback.setConnectivityStatus(
+ /* noDefaultNetwork= */ true,
+ /* noValidatedNetwork= */ true,
+ /* noNetworksAvailable= */ true);
+ mTestableLooper.processAllMessages();
+ assertThat(String.valueOf(mTile.getState().secondaryLabel))
+ .isEqualTo(mContext.getString(R.string.quick_settings_networks_unavailable));
+ assertThat(mTile.getLastTileState()).isEqualTo(1);
+ }
+
+ @Test
+ public void setConnectivityStatus_defaultNetworkExists_notUpdateTile() {
+ mTile.mSignalCallback.setConnectivityStatus(
+ /* noDefaultNetwork= */ false,
+ /* noValidatedNetwork= */ true,
+ /* noNetworksAvailable= */ true);
+ mTestableLooper.processAllMessages();
+ assertThat(String.valueOf(mTile.getState().secondaryLabel))
+ .isNotEqualTo(mContext.getString(R.string.quick_settings_networks_unavailable));
+ assertThat(String.valueOf(mTile.getState().secondaryLabel))
+ .isNotEqualTo(mContext.getString(R.string.quick_settings_networks_available));
+ assertThat(mTile.getLastTileState()).isEqualTo(-1);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index ed1a13b36d6c..20c6d9adc300 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -91,6 +91,10 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
+ private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
+ @Mock
+ private lateinit var iconManager: StatusBarIconController.TintedIconManager
+ @Mock
private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock
private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@@ -169,6 +173,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
+ whenever(iconManagerFactory.create(any())).thenReturn(iconManager)
+
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true)
whenever(featureFlags.isEnabled(Flags.NEW_HEADER)).thenReturn(true)
@@ -178,6 +184,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
controller = LargeScreenShadeHeaderController(
view,
statusBarIconController,
+ iconManagerFactory,
privacyIconsController,
insetsProvider,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 02b26dbbc32d..eeb61bc8a0f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -43,6 +43,8 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var view: View
@Mock private lateinit var statusIcons: StatusIconContainer
@Mock private lateinit var statusBarIconController: StatusBarIconController
+ @Mock private lateinit var iconManagerFactory: StatusBarIconController.TintedIconManager.Factory
+ @Mock private lateinit var iconManager: StatusBarIconController.TintedIconManager
@Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@Mock private lateinit var featureFlags: FeatureFlags
@@ -91,10 +93,12 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(variableDateViewControllerFactory.create(any()))
.thenReturn(variableDateViewController)
+ whenever(iconManagerFactory.create(any())).thenReturn(iconManager)
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(false)
mLargeScreenShadeHeaderController = LargeScreenShadeHeaderController(
view,
statusBarIconController,
+ iconManagerFactory,
privacyIconsController,
insetsProvider,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 95211c0c8ec8..32fa6e45bc37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,7 +47,6 @@ import android.content.ContentResolver;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.graphics.PointF;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -764,10 +763,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Test
public void testSetDozing_notifiesNsslAndStateController() {
- mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */,
- null /* touch */);
- verify(mNotificationStackScrollLayoutController)
- .setDozing(eq(true), eq(false), eq(null));
+ mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */);
+ verify(mNotificationStackScrollLayoutController).setDozing(eq(true), eq(false));
assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f);
}
@@ -1219,9 +1216,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
clearInvocations(mKeyguardStatusViewController);
- mNotificationPanelViewController.setDozing(true, false, null);
-
- verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate= */ true);
}
@Test
@@ -1233,7 +1229,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
- mNotificationPanelViewController.setDozing(true, false, null);
+ mNotificationPanelViewController.setDozing(true, false);
verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
}
@@ -1547,8 +1543,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
mNotificationPanelViewController.setDozing(
/* dozing= */ dozing,
- /* animate= */ false,
- /* wakeUpTouchLocation= */ new PointF()
+ /* animate= */ false
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
new file mode 100644
index 000000000000..9393a4f4eead
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.rotation
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display
+import android.view.WindowInsetsController
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class RotationButtonControllerTest : SysuiTestCase() {
+
+ private lateinit var mController: RotationButtonController
+
+ @Before
+ fun setUp() {
+ mController = RotationButtonController(
+ mContext,
+ /* lightIconColor = */ 0,
+ /* darkIconColor = */ 0,
+ /* iconCcwStart0ResId = */ 0,
+ /* iconCcwStart90ResId = */ 0,
+ /* iconCwStart0ResId = */ 0,
+ /* iconCwStart90ResId = */ 0
+ ) { 0 }
+ }
+
+ @Test
+ fun ifGestural_showRotationSuggestion() {
+ mController.onNavigationBarWindowVisibilityChange( /* showing = */ false)
+ mController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
+ mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON)
+ mController.onTaskbarStateChange( /* visible = */ false, /* stashed = */ false)
+ assertThat(mController.canShowRotationButton()).isFalse()
+
+ mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL)
+
+ assertThat(mController.canShowRotationButton()).isTrue()
+ }
+
+ @Test
+ fun ifTaskbarVisible_showRotationSuggestion() {
+ mController.onNavigationBarWindowVisibilityChange( /* showing = */ false)
+ mController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
+ mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON)
+ mController.onTaskbarStateChange( /* visible = */ false, /* stashed = */ false)
+ assertThat(mController.canShowRotationButton()).isFalse()
+
+ mController.onTaskbarStateChange( /* visible = */ true, /* stashed = */ false)
+
+ assertThat(mController.canShowRotationButton()).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index be631afdd1a9..1d8e5dec5c50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -38,8 +38,8 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -89,8 +89,8 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
val listener = mock(StatusBarStateController.StateListener::class.java)
controller.addCallback(listener)
- controller.setDozeAmount(0.5f, false /* animated */)
- controller.setDozeAmount(0.5f, false /* animated */)
+ controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
+ controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
verify(listener).onDozeAmountChanged(eq(0.5f), anyFloat())
}
@@ -135,7 +135,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
@Test
fun testSetDozeAmount_immediatelyChangesDozeAmount_lockscreenTransitionFromAod() {
// Put controller in AOD state
- controller.setDozeAmount(1f, false)
+ controller.setAndInstrumentDozeAmount(null, 1f, false)
// When waking from doze, CentralSurfaces#updateDozingState will update the dozing state
// before the doze amount changes
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 214ba16dfc44..64d025628754 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -98,7 +98,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
@Test
fun testPrepareDialogForApp_onlyDefaultChannel() {
- group.channels = listOf(channelDefault)
+ group.addChannel(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
setOf(channelDefault), appIcon, clickListener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index f8b39e8cff71..137842ef314f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -28,7 +28,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -58,8 +57,8 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon;
-import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import org.junit.Assert;
@@ -260,17 +259,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
- public void setNeedsRedactionFreesViewWhenFalse() throws Exception {
- ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
- row.setNeedsRedaction(true);
- row.getPublicLayout().setVisibility(View.GONE);
-
- row.setNeedsRedaction(false);
- TestableLooper.get(this).processAllMessages();
- assertNull(row.getPublicLayout().getContractedChild());
- }
-
- @Test
public void testAboveShelfChangedListenerCalled() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 11e502fc79bf..6cf1a12d94f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -47,7 +47,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -89,7 +88,9 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
@Mock
private StatusBarIconController mStatusBarIconController;
@Mock
- private FeatureFlags mFeatureFlags;
+ private StatusBarIconController.TintedIconManager.Factory mIconManagerFactory;
+ @Mock
+ private StatusBarIconController.TintedIconManager mIconManager;
@Mock
private BatteryMeterViewController mBatteryMeterViewController;
@Mock
@@ -129,6 +130,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
+ when(mIconManagerFactory.create(any())).thenReturn(mIconManager);
+
allowTestableLooperAsMainThread();
TestableLooper.get(this).runWithLooper(() -> {
mKeyguardStatusBarView =
@@ -148,7 +151,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
mBatteryController,
mUserInfoController,
mStatusBarIconController,
- new StatusBarIconController.TintedIconManager.Factory(mFeatureFlags),
+ mIconManagerFactory,
mBatteryMeterViewController,
mNotificationPanelViewStateProvider,
mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 0f1c40bacb7b..a6b7e5103c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -40,12 +40,16 @@ import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconMana
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import javax.inject.Provider;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -67,7 +71,11 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
@Test
public void testSetCalledOnAdd_DarkIconManager() {
LinearLayout layout = new LinearLayout(mContext);
- TestDarkIconManager manager = new TestDarkIconManager(layout, mock(FeatureFlags.class));
+ TestDarkIconManager manager = new TestDarkIconManager(
+ layout,
+ mock(FeatureFlags.class),
+ mock(StatusBarPipelineFlags.class),
+ () -> mock(WifiViewModel.class));
testCallOnAdd_forManager(manager);
}
@@ -104,8 +112,12 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
private static class TestDarkIconManager extends DarkIconManager
implements TestableIconManager {
- TestDarkIconManager(LinearLayout group, FeatureFlags featureFlags) {
- super(group, featureFlags);
+ TestDarkIconManager(
+ LinearLayout group,
+ FeatureFlags featureFlags,
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Provider<WifiViewModel> wifiViewModelProvider) {
+ super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
}
@Override
@@ -123,7 +135,7 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
}
@Override
- protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+ protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) {
StatusBarWifiView mock = mock(StatusBarWifiView.class);
mGroup.addView(mock, index);
return mock;
@@ -140,7 +152,10 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
private static class TestIconManager extends IconManager implements TestableIconManager {
TestIconManager(ViewGroup group) {
- super(group, mock(FeatureFlags.class));
+ super(group,
+ mock(FeatureFlags.class),
+ mock(StatusBarPipelineFlags.class),
+ () -> mock(WifiViewModel.class));
}
@Override
@@ -158,7 +173,7 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
}
@Override
- protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+ protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) {
StatusBarWifiView mock = mock(StatusBarWifiView.class);
mGroup.addView(mock, index);
return mock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index de43a1fabab6..2b805089a430 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -37,7 +37,6 @@ import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Intent;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -135,8 +134,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private PendingIntent mContentIntent;
@Mock
- private Intent mContentIntentInner;
- @Mock
private OnUserInteractionCallback mOnUserInteractionCallback;
@Mock
private Runnable mFutureDismissalRunnable;
@@ -159,7 +156,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
when(mContentIntent.isActivity()).thenReturn(true);
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
- when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
NotificationTestHelper notificationTestHelper = new NotificationTestHelper(
mContext,
@@ -374,7 +370,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
eq(entry.getKey()), any(NotificationVisibility.class));
// The content intent should NOT be sent on click.
- verify(mContentIntent).getIntent();
verify(mContentIntent).isActivity();
verifyNoMoreInteractions(mContentIntent);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 4c8599d99ddd..ceaceb4695b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -111,6 +111,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Mock
private NotificationPanelViewController mNotificationPanelViewController;
@Mock
+ private StatusBarIconController.DarkIconManager.Factory mIconManagerFactory;
+ @Mock
+ private StatusBarIconController.DarkIconManager mIconManager;
+ @Mock
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock
private DumpManager mDumpManager;
@@ -424,6 +428,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
+ when(mIconManagerFactory.create(any())).thenReturn(mIconManager);
mSecureSettings = mock(SecureSettings.class);
setUpNotificationIconAreaController();
@@ -436,6 +441,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
new PanelExpansionStateManager(),
mock(FeatureFlags.class),
mStatusBarIconController,
+ mIconManagerFactory,
mStatusBarHideIconsForBouncerManager,
mKeyguardStateController,
mNotificationPanelViewController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
deleted file mode 100644
index 7b492cb7ddd1..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline
-
-import android.net.NetworkCapabilities
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
-import com.android.systemui.util.mockito.mock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.InternalCoroutinesApi
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.yield
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.`when` as whenever
-
-@OptIn(InternalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class ConnectivityInfoProcessorTest : SysuiTestCase() {
-
- private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
-
- @Before
- fun setUp() {
- whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
- }
-
- @Test
- fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
- // GIVEN a processor hooked up to a collector
- val scope = CoroutineScope(Dispatchers.Unconfined)
- val collector = FakeConnectivityInfoCollector()
- val processor = ConnectivityInfoProcessor(
- collector,
- context,
- scope,
- statusBarPipelineFlags,
- mock(),
- )
-
- var mostRecentValue: ProcessedConnectivityInfo? = null
- val job = launch(start = CoroutineStart.UNDISPATCHED) {
- processor.processedInfoFlow.collect {
- mostRecentValue = it
- }
- }
-
- // WHEN the collector emits a value
- val networkCapabilityInfo = mapOf(
- 10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
- )
- collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
- // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
- // this test. So, our test needs to yield to let the job run.
- // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
- yield()
-
- // THEN the processor receives it
- assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
-
- job.cancel()
- scope.cancel()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index df389bc52664..6b8d4aa7c51f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -17,21 +17,22 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
/** Fake implementation of [WifiRepository] exposing set methods for all the flows. */
class FakeWifiRepository : WifiRepository {
- private val _wifiModel: MutableStateFlow<WifiModel?> = MutableStateFlow(null)
- override val wifiModel: Flow<WifiModel?> = _wifiModel
+ private val _wifiNetwork: MutableStateFlow<WifiNetworkModel> =
+ MutableStateFlow(WifiNetworkModel.Inactive)
+ override val wifiNetwork: Flow<WifiNetworkModel> = _wifiNetwork
private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT)
override val wifiActivity: Flow<WifiActivityModel> = _wifiActivity
- fun setWifiModel(wifiModel: WifiModel?) {
- _wifiModel.value = wifiModel
+ fun setWifiNetwork(wifiNetworkModel: WifiNetworkModel) {
+ _wifiNetwork.value = wifiNetworkModel
}
fun setWifiActivity(activity: WifiActivityModel) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepoTest.kt
deleted file mode 100644
index 6edf76ce77c9..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/NetworkCapabilitiesRepoTest.kt
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.pipeline.wifi.data.repository
-
-import android.net.ConnectivityManager
-import android.net.ConnectivityManager.NetworkCallback
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED
-import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
-import android.net.NetworkRequest
-import android.test.suitebuilder.annotation.SmallTest
-import android.testing.AndroidTestingRunner
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
-
-// TODO(b/240619365): Update this test to use `runTest` when we update the testing library
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class NetworkCapabilitiesRepoTest : SysuiTestCase() {
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var logger: ConnectivityPipelineLogger
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- }
-
- @Test
- fun testOnCapabilitiesChanged_oneNewNetwork_networkStored() = runBlocking {
- // GIVEN a repo hooked up to [ConnectivityManager]
- val scope = CoroutineScope(Dispatchers.Unconfined)
- val repo = NetworkCapabilitiesRepo(
- connectivityManager = connectivityManager,
- scope = scope,
- logger = logger,
- )
-
- val job = launch(start = CoroutineStart.UNDISPATCHED) {
- repo.dataStream.collect {
- }
- }
-
- val callback: NetworkCallback = withArgCaptor {
- verify(connectivityManager)
- .registerNetworkCallback(any(NetworkRequest::class.java), capture())
- }
-
- // WHEN a new network is added
- callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
-
- val currentMap = repo.dataStream.value
-
- // THEN it is emitted from the flow
- assertThat(currentMap[NET_1_ID]?.network).isEqualTo(NET_1)
- assertThat(currentMap[NET_1_ID]?.capabilities).isEqualTo(NET_1_CAPS)
-
- job.cancel()
- scope.cancel()
- }
-
- @Test
- fun testOnCapabilitiesChanged_twoNewNetworks_bothStored() = runBlocking {
- // GIVEN a repo hooked up to [ConnectivityManager]
- val scope = CoroutineScope(Dispatchers.Unconfined)
- val repo = NetworkCapabilitiesRepo(
- connectivityManager = connectivityManager,
- scope = scope,
- logger = logger,
- )
-
- val job = launch(start = CoroutineStart.UNDISPATCHED) {
- repo.dataStream.collect {
- }
- }
-
- val callback: NetworkCallback = withArgCaptor {
- verify(connectivityManager)
- .registerNetworkCallback(any(NetworkRequest::class.java), capture())
- }
-
- // WHEN two new networks are added
- callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
- callback.onCapabilitiesChanged(NET_2, NET_2_CAPS)
-
- val currentMap = repo.dataStream.value
-
- // THEN the current state of the flow reflects 2 networks
- assertThat(currentMap[NET_1_ID]?.network).isEqualTo(NET_1)
- assertThat(currentMap[NET_1_ID]?.capabilities).isEqualTo(NET_1_CAPS)
- assertThat(currentMap[NET_2_ID]?.network).isEqualTo(NET_2)
- assertThat(currentMap[NET_2_ID]?.capabilities).isEqualTo(NET_2_CAPS)
-
- job.cancel()
- scope.cancel()
- }
-
- @Test
- fun testOnCapabilitesChanged_newCapabilitiesForExistingNetwork_areCaptured() = runBlocking {
- // GIVEN a repo hooked up to [ConnectivityManager]
- val scope = CoroutineScope(Dispatchers.Unconfined)
- val repo = NetworkCapabilitiesRepo(
- connectivityManager = connectivityManager,
- scope = scope,
- logger = logger,
- )
-
- val job = launch(start = CoroutineStart.UNDISPATCHED) {
- repo.dataStream.collect {
- }
- }
-
- val callback: NetworkCallback = withArgCaptor {
- verify(connectivityManager)
- .registerNetworkCallback(any(NetworkRequest::class.java), capture())
- }
-
- // WHEN a network is added, and then its capabilities are changed
- callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
- callback.onCapabilitiesChanged(NET_1, NET_2_CAPS)
-
- val currentMap = repo.dataStream.value
-
- // THEN the current state of the flow reflects the new capabilities
- assertThat(currentMap[NET_1_ID]?.capabilities).isEqualTo(NET_2_CAPS)
-
- job.cancel()
- scope.cancel()
- }
-
- @Test
- fun testOnLost_networkIsRemoved() = runBlocking {
- // GIVEN a repo hooked up to [ConnectivityManager]
- val scope = CoroutineScope(Dispatchers.Unconfined)
- val repo = NetworkCapabilitiesRepo(
- connectivityManager = connectivityManager,
- scope = scope,
- logger = logger,
- )
-
- val job = launch(start = CoroutineStart.UNDISPATCHED) {
- repo.dataStream.collect {
- }
- }
-
- val callback: NetworkCallback = withArgCaptor {
- verify(connectivityManager)
- .registerNetworkCallback(any(NetworkRequest::class.java), capture())
- }
-
- // WHEN two new networks are added, and one is removed
- callback.onCapabilitiesChanged(NET_1, NET_1_CAPS)
- callback.onCapabilitiesChanged(NET_2, NET_2_CAPS)
- callback.onLost(NET_1)
-
- val currentMap = repo.dataStream.value
-
- // THEN the current state of the flow reflects only the remaining network
- assertThat(currentMap[NET_1_ID]).isNull()
- assertThat(currentMap[NET_2_ID]?.network).isEqualTo(NET_2)
- assertThat(currentMap[NET_2_ID]?.capabilities).isEqualTo(NET_2_CAPS)
-
- job.cancel()
- scope.cancel()
- }
-
- @Test
- fun testOnLost_noNetworks_doesNotCrash() = runBlocking {
- // GIVEN a repo hooked up to [ConnectivityManager]
- val scope = CoroutineScope(Dispatchers.Unconfined)
- val repo = NetworkCapabilitiesRepo(
- connectivityManager = connectivityManager,
- scope = scope,
- logger = logger,
- )
-
- val job = launch(start = CoroutineStart.UNDISPATCHED) {
- repo.dataStream.collect {
- }
- }
-
- val callback: NetworkCallback = withArgCaptor {
- verify(connectivityManager)
- .registerNetworkCallback(any(NetworkRequest::class.java), capture())
- }
-
- // WHEN no networks are added, and one is removed
- callback.onLost(NET_1)
-
- val currentMap = repo.dataStream.value
-
- // THEN the current state of the flow shows no networks
- assertThat(currentMap).isEmpty()
-
- job.cancel()
- scope.cancel()
- }
-
- private val NET_1_ID = 100
- private val NET_1 = mock<Network>().also {
- whenever(it.getNetId()).thenReturn(NET_1_ID)
- }
- private val NET_2_ID = 200
- private val NET_2 = mock<Network>().also {
- whenever(it.getNetId()).thenReturn(NET_2_ID)
- }
-
- private val NET_1_CAPS = NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_CELLULAR)
- .addCapability(NET_CAPABILITY_VALIDATED)
- .build()
-
- private val NET_2_CAPS = NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_WIFI)
- .addCapability(NET_CAPABILITY_NOT_METERED)
- .addCapability(NET_CAPABILITY_VALIDATED)
- .build()
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
index 8b61364a2ac9..d0a38084af76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
@@ -16,16 +16,26 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.vcn.VcnTransportInfo
+import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.TrafficStateCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
@@ -38,6 +48,7 @@ import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -47,6 +58,7 @@ class WifiRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: WifiRepositoryImpl
@Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var wifiManager: WifiManager
private lateinit var executor: Executor
@@ -54,11 +66,347 @@ class WifiRepositoryImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
executor = FakeExecutor(FakeSystemClock())
+
+ underTest = WifiRepositoryImpl(
+ connectivityManager,
+ wifiManager,
+ executor,
+ logger,
+ )
+ }
+
+ @Test
+ fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val wifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+ val network = mock<Network>().apply {
+ whenever(this.getNetId()).thenReturn(NETWORK_ID)
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(network, createWifiNetworkCapabilities(wifiInfo))
+
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val wifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(false)
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val capabilities = mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_nonPrimaryCellularVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val wifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(false)
+ }
+ val capabilities = mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(wifiInfo))
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_cellularNotVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val capabilities = mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(mock())
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // Start with the original network
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+ // WHEN we update to a new primary network
+ val newNetworkId = 456
+ val newNetwork = mock<Network>().apply {
+ whenever(this.getNetId()).thenReturn(newNetworkId)
+ }
+ val newSsid = "CD"
+ val newWifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(newSsid)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(
+ newNetwork, createWifiNetworkCapabilities(newWifiInfo)
+ )
+
+ // THEN we use the new network
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(newNetworkId)
+ assertThat(latestActive.ssid).isEqualTo(newSsid)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_newNonPrimaryWifiNetwork_flowHasOldNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // Start with the original network
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+ // WHEN we notify of a new but non-primary network
+ val newNetworkId = 456
+ val newNetwork = mock<Network>().apply {
+ whenever(this.getNetId()).thenReturn(newNetworkId)
+ }
+ val newSsid = "EF"
+ val newWifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(newSsid)
+ whenever(this.isPrimary).thenReturn(false)
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(
+ newNetwork, createWifiNetworkCapabilities(newWifiInfo)
+ )
+
+ // THEN we still use the original network
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_newNetworkCapabilities_flowHasNewData() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ val wifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+
+ // Start with the original network
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+ // WHEN we keep the same network ID but change the SSID
+ val newSsid = "CD"
+ val newWifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(newSsid)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(newWifiInfo))
+
+ // THEN we've updated to the new SSID
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(newSsid)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ // WHEN we receive #onLost without any #onCapabilitiesChanged beforehand
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN there's no crash and we still have no network
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we lose our current network
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN we update to no network
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we lose an unknown network
+ val unknownNetwork = mock<Network>().apply {
+ whenever(this.getNetId()).thenReturn(543)
+ }
+ getNetworkCallback().onLost(unknownNetwork)
+
+ // THEN we still have our previous network
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_notCurrentNetworkLost_flowHasCurrentNetwork() = runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest
+ .wifiNetwork
+ .onEach { latest = it }
+ .launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we update to a new network...
+ val newNetworkId = 89
+ val newNetwork = mock<Network>().apply {
+ whenever(this.getNetId()).thenReturn(newNetworkId)
+ }
+ getNetworkCallback().onCapabilitiesChanged(
+ newNetwork, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
+ )
+ // ...and lose the old network
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN we still have the new network
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(newNetworkId)
+
+ job.cancel()
}
@Test
fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) {
underTest = WifiRepositoryImpl(
+ connectivityManager,
wifiManager = null,
executor,
logger,
@@ -77,12 +425,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Test
fun wifiActivity_callbackGivesNone_activityFlowHasNone() = runBlocking(IMMEDIATE) {
- underTest = WifiRepositoryImpl(
- wifiManager,
- executor,
- logger,
- )
-
var latest: WifiActivityModel? = null
val job = underTest
.wifiActivity
@@ -100,12 +442,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Test
fun wifiActivity_callbackGivesIn_activityFlowHasIn() = runBlocking(IMMEDIATE) {
- underTest = WifiRepositoryImpl(
- wifiManager,
- executor,
- logger,
- )
-
var latest: WifiActivityModel? = null
val job = underTest
.wifiActivity
@@ -123,12 +459,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Test
fun wifiActivity_callbackGivesOut_activityFlowHasOut() = runBlocking(IMMEDIATE) {
- underTest = WifiRepositoryImpl(
- wifiManager,
- executor,
- logger,
- )
-
var latest: WifiActivityModel? = null
val job = underTest
.wifiActivity
@@ -146,12 +476,6 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Test
fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() = runBlocking(IMMEDIATE) {
- underTest = WifiRepositoryImpl(
- wifiManager,
- executor,
- logger,
- )
-
var latest: WifiActivityModel? = null
val job = underTest
.wifiActivity
@@ -170,6 +494,30 @@ class WifiRepositoryImplTest : SysuiTestCase() {
verify(wifiManager).registerTrafficStateCallback(any(), callbackCaptor.capture())
return callbackCaptor.value!!
}
+
+ private fun getNetworkCallback(): ConnectivityManager.NetworkCallback {
+ val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
+ verify(connectivityManager).registerNetworkCallback(any(), callbackCaptor.capture())
+ return callbackCaptor.value!!
+ }
+
+ private fun createWifiNetworkCapabilities(wifiInfo: WifiInfo) =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(wifiInfo)
+ }
+
+ private companion object {
+ const val NETWORK_ID = 45
+ val NETWORK = mock<Network>().apply {
+ whenever(this.getNetId()).thenReturn(NETWORK_ID)
+ }
+ const val SSID = "AB"
+ val PRIMARY_WIFI_INFO: WifiInfo = mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+ }
}
private val IMMEDIATE = Dispatchers.Main.immediate
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
index c52f347a605a..5f1b1dbb19dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
@@ -47,7 +47,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiModel(WifiModel(ssid = "AB"))
+ repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
var latest: Boolean? = null
@@ -63,7 +63,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiModel(WifiModel(ssid = "AB"))
+ repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
var latest: Boolean? = null
@@ -79,7 +79,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
- repository.setWifiModel(WifiModel(ssid = "AB"))
+ repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
var latest: Boolean? = null
@@ -95,7 +95,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
- repository.setWifiModel(WifiModel(ssid = "AB"))
+ repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
var latest: Boolean? = null
@@ -111,7 +111,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
- repository.setWifiModel(WifiModel(ssid = null))
+ repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
var latest: Boolean? = null
@@ -127,7 +127,7 @@ class WifiInteractorTest : SysuiTestCase() {
@Test
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
- repository.setWifiModel(WifiModel(ssid = "AB"))
+ repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
var latest: Boolean? = null
val job = underTest
@@ -158,6 +158,10 @@ class WifiInteractorTest : SysuiTestCase() {
job.cancel()
}
+
+ companion object {
+ val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB")
+ }
}
private val IMMEDIATE = Dispatchers.Main.immediate
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
new file mode 100644
index 000000000000..3c200a5da4fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.ui.view
+
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.lifecycle.InstantTaskExecutorRule
+import com.android.systemui.util.Assert
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+@RunWithLooper
+class ModernStatusBarWifiViewTest : SysuiTestCase() {
+
+ @JvmField @Rule
+ val instantTaskExecutor = InstantTaskExecutorRule()
+
+ @Before
+ fun setUp() {
+ Assert.setTestThread(Thread.currentThread())
+ }
+
+ @Test
+ fun constructAndBind_hasCorrectSlot() {
+ val view = ModernStatusBarWifiView.constructAndBind(
+ context, "slotName", mock()
+ )
+
+ assertThat(view.slot).isEqualTo("slotName")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index e9259b071e0b..c79073409883 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -18,9 +18,10 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
@@ -43,6 +44,7 @@ class WifiViewModelTest : SysuiTestCase() {
private lateinit var underTest: WifiViewModel
+ @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var logger: ConnectivityPipelineLogger
@Mock private lateinit var constants: WifiConstants
private lateinit var repository: FakeWifiRepository
@@ -55,13 +57,14 @@ class WifiViewModelTest : SysuiTestCase() {
interactor = WifiInteractor(repository)
underTest = WifiViewModel(
- constants,
- logger,
- interactor
+ statusBarPipelineFlags,
+ constants,
+ logger,
+ interactor
)
// Set up with a valid SSID
- repository.setWifiModel(WifiModel(ssid = "AB"))
+ repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = "AB"))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
new file mode 100644
index 000000000000..15ba67205034
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import java.util.concurrent.atomic.AtomicLong
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class IpcSerializerTest : SysuiTestCase() {
+
+ private val serializer = IpcSerializer()
+
+ @Test
+ fun serializeManyIncomingIpcs(): Unit = runBlocking(Dispatchers.Main.immediate) {
+ val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
+ withContext(Dispatchers.IO) {
+ val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
+ // First, launch many serialization requests in parallel
+ repeat(100_000) {
+ launch(Dispatchers.Unconfined) {
+ val enqueuedTime = System.currentTimeMillis()
+ serializer.runSerialized {
+ val last = lastEvaluatedTime.getAndSet(enqueuedTime)
+ assertTrue(
+ "expected $last less than or equal to $enqueuedTime ",
+ last <= enqueuedTime,
+ )
+ }
+ }
+ }
+ // Then, process them all in the order they came in.
+ processor.start()
+ }
+ // All done, stop processing
+ processor.cancel()
+ }
+
+ @Test(timeout = 5000)
+ fun serializeOnOneThread_doesNotDeadlock() = runBlocking {
+ val job = launch { serializer.process() }
+ repeat(100) {
+ serializer.runSerializedBlocking { }
+ }
+ job.cancel()
+ }
+}
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 8f2b715ba051..5d63632725c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -135,6 +135,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -225,6 +226,8 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private ShellInit mShellInit;
@Mock
+ private ShellCommandHandler mShellCommandHandler;
+ @Mock
private ShellController mShellController;
@Mock
private Bubbles.BubbleExpandListener mBubbleExpandListener;
@@ -349,6 +352,7 @@ public class BubblesTest extends SysuiTestCase {
mBubbleController = new TestableBubbleController(
mContext,
mShellInit,
+ mShellCommandHandler,
mShellController,
mBubbleData,
mFloatingContentCoordinator,
@@ -389,7 +393,6 @@ public class BubblesTest extends SysuiTestCase {
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
- mDumpManager,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -1395,6 +1398,33 @@ public class BubblesTest extends SysuiTestCase {
assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE);
}
+ /**
+ * Test to verify behavior for following situation:
+ * <ul>
+ * <li>status bar shade state is set to <code>false</code></li>
+ * <li>there is a bubble pending to be expanded</li>
+ * </ul>
+ * Test that duplicate status bar state updates to <code>false</code> do not clear the
+ * pending bubble to be
+ * expanded.
+ */
+ @Test
+ public void testOnStatusBarStateChanged_statusBarChangeDoesNotClearExpandingBubble() {
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.onStatusBarStateChanged(false);
+ // Set the bubble to expand once status bar state changes
+ mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
+ // Check that stack is currently collapsed
+ assertStackCollapsed();
+ // Post status bar state change update with the same value
+ mBubbleController.onStatusBarStateChanged(false);
+ // Stack should remain collapsedb
+ assertStackCollapsed();
+ // Post status bar state change which should trigger bubble to expand
+ mBubbleController.onStatusBarStateChanged(true);
+ assertStackExpanded();
+ }
+
@Test
public void testSetShouldAutoExpand_notifiesFlagChanged() {
mBubbleController.updateBubble(mBubbleEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 880ad187f910..6357a09eb196 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -38,6 +38,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -51,6 +52,7 @@ public class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
BubbleData data,
FloatingContentCoordinator floatingContentCoordinator,
@@ -71,12 +73,12 @@ public class TestableBubbleController extends BubbleController {
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
- super(context, shellInit, shellController, data, Runnable::run, floatingContentCoordinator,
- dataRepository, statusBarService, windowManager, windowManagerShellWrapper,
- userManager, launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer,
- positioner, displayController, oneHandedOptional, dragAndDropController,
- shellMainExecutor, shellMainHandler, new SyncExecutor(), taskViewTransitions,
- syncQueue);
+ super(context, shellInit, shellCommandHandler, shellController, data, Runnable::run,
+ floatingContentCoordinator, dataRepository, statusBarService, windowManager,
+ windowManagerShellWrapper, userManager, launcherApps, bubbleLogger,
+ taskStackListener, shellTaskOrganizer, positioner, displayController,
+ oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler,
+ new SyncExecutor(), taskViewTransitions, syncQueue);
setInflateSynchronously(true);
onInit();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 9c2136675dfa..da33fa62a9ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -28,10 +28,10 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.onehanded.OneHanded;
@@ -72,7 +72,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock OneHanded mOneHanded;
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
@Mock ProtoTracer mProtoTracer;
- @Mock UserInfoController mUserInfoController;
+ @Mock UserTracker mUserTracker;
@Mock ShellExecutor mSysUiMainExecutor;
@Before
@@ -83,7 +83,7 @@ public class WMShellTest extends SysuiTestCase {
Optional.of(mSplitScreen), Optional.of(mOneHanded), mCommandQueue,
mConfigurationController, mKeyguardStateController, mKeyguardUpdateMonitor,
mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
- mUserInfoController, mSysUiMainExecutor);
+ mUserTracker, mSysUiMainExecutor);
}
@Test
diff --git a/services/OWNERS b/services/OWNERS
index 67cee5517913..495c0737e599 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,4 +1,4 @@
-per-file Android.bp = file:platform/build/soong:/OWNERS
+per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
# art-team@ manages the system server profile
per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
diff --git a/services/api/OWNERS b/services/api/OWNERS
index a6093900c635..e10440c1aed5 100644
--- a/services/api/OWNERS
+++ b/services/api/OWNERS
@@ -1,4 +1,4 @@
-per-file Android.bp = file:platform/build/soong:/OWNERS
+per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
# API changes are managed via Prolog rules, not OWNERS
*
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 0b858cf38d44..eb1d2d762776 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2987,8 +2987,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mBarringInfo=" + mBarringInfo.get(i));
pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState[i]);
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
- pw.println("mIsDataEnabled=" + mIsDataEnabled);
- pw.println("mDataEnabledReason=" + mDataEnabledReason);
+ pw.println("mIsDataEnabled=" + mIsDataEnabled[i]);
+ pw.println("mDataEnabledReason=" + mDataEnabledReason[i]);
pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 297e6a2c5fc7..734f45523618 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -454,6 +454,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -5501,7 +5502,7 @@ public class ActivityManagerService extends IActivityManager.Stub
IIntentSender pendingResult, int matchFlags) {
enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT,
"queryIntentComponentsForIntentSender()");
- Preconditions.checkNotNull(pendingResult);
+ Objects.requireNonNull(pendingResult);
final PendingIntentRecord res;
try {
res = (PendingIntentRecord) pendingResult;
@@ -5513,17 +5514,19 @@ public class ActivityManagerService extends IActivityManager.Stub
return null;
}
final int userId = res.key.userId;
+ final int uid = res.uid;
+ final String resolvedType = res.key.requestResolvedType;
switch (res.key.type) {
case ActivityManager.INTENT_SENDER_ACTIVITY:
- return new ParceledListSlice<>(mContext.getPackageManager()
- .queryIntentActivitiesAsUser(intent, matchFlags, userId));
+ return new ParceledListSlice<>(mPackageManagerInt.queryIntentActivities(
+ intent, resolvedType, matchFlags, uid, userId));
case ActivityManager.INTENT_SENDER_SERVICE:
case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
- return new ParceledListSlice<>(mContext.getPackageManager()
- .queryIntentServicesAsUser(intent, matchFlags, userId));
+ return new ParceledListSlice<>(mPackageManagerInt.queryIntentServices(
+ intent, matchFlags, uid, userId));
case ActivityManager.INTENT_SENDER_BROADCAST:
- return new ParceledListSlice<>(mContext.getPackageManager()
- .queryBroadcastReceiversAsUser(intent, matchFlags, userId));
+ return new ParceledListSlice<>(mPackageManagerInt.queryIntentReceivers(
+ intent, resolvedType, matchFlags, uid, userId, false));
default: // ActivityManager.INTENT_SENDER_ACTIVITY_RESULT
throw new IllegalStateException("Unsupported intent sender type: " + res.key.type);
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 1302e226eba3..134e2061c090 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -502,6 +502,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
GamePackageConfiguration(PackageManager packageManager, String packageName, int userId) {
mPackageName = packageName;
+
+ // set flag default values
+ mPerfModeOptedIn = false;
+ mBatteryModeOptedIn = false;
+ mAllowDownscale = true;
+ mAllowAngle = true;
+ mAllowFpsOverride = true;
+
try {
final ApplicationInfo ai = packageManager.getApplicationInfoAsUser(packageName,
PackageManager.GET_META_DATA, userId);
@@ -511,12 +519,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
- } else {
- mPerfModeOptedIn = false;
- mBatteryModeOptedIn = false;
- mAllowDownscale = true;
- mAllowAngle = true;
- mAllowFpsOverride = true;
}
}
} catch (NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a5bcb0517d25..248e35e6964d 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -63,7 +63,6 @@ import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
-import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
import static android.app.AppOpsManager.extractUidStateFromKey;
@@ -563,6 +562,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public ArrayMap<String, Ops> pkgOps;
// true indicates there is an interested observer, false there isn't but it has such an op
+ //TODO: Move foregroundOps and hasForegroundWatchers into the AppOpsServiceInterface.
public SparseBooleanArray foregroundOps;
public boolean hasForegroundWatchers;
@@ -658,48 +658,24 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
return mode;
}
- private void evalForegroundWatchers(int op, SparseArray<ArraySet<ModeCallback>> watchers,
- SparseBooleanArray which) {
- boolean curValue = which.get(op, false);
- ArraySet<ModeCallback> callbacks = watchers.get(op);
- if (callbacks != null) {
- for (int cbi = callbacks.size() - 1; !curValue && cbi >= 0; cbi--) {
- if ((callbacks.valueAt(cbi).mFlags
- & AppOpsManager.WATCH_FOREGROUND_CHANGES) != 0) {
- hasForegroundWatchers = true;
- curValue = true;
- }
+ public void evalForegroundOps() {
+ foregroundOps = null;
+ foregroundOps = mAppOpsServiceInterface.evalForegroundUidOps(uid, foregroundOps);
+ if (pkgOps != null) {
+ for (int i = pkgOps.size() - 1; i >= 0; i--) {
+ foregroundOps = mAppOpsServiceInterface
+ .evalForegroundPackageOps(pkgOps.valueAt(i).packageName, foregroundOps);
}
}
- which.put(op, curValue);
- }
-
- public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) {
- SparseBooleanArray which = null;
hasForegroundWatchers = false;
- final SparseIntArray opModes = getNonDefaultUidModes();
- for (int i = opModes.size() - 1; i >= 0; i--) {
- if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
- if (which == null) {
- which = new SparseBooleanArray();
- }
- evalForegroundWatchers(opModes.keyAt(i), watchers, which);
- }
- }
- if (pkgOps != null) {
- for (int i = pkgOps.size() - 1; i >= 0; i--) {
- Ops ops = pkgOps.valueAt(i);
- for (int j = ops.size() - 1; j >= 0; j--) {
- if (ops.valueAt(j).getMode() == AppOpsManager.MODE_FOREGROUND) {
- if (which == null) {
- which = new SparseBooleanArray();
- }
- evalForegroundWatchers(ops.keyAt(j), watchers, which);
- }
+ if (foregroundOps != null) {
+ for (int i = 0; i < foregroundOps.size(); i++) {
+ if (foregroundOps.valueAt(i)) {
+ hasForegroundWatchers = true;
+ break;
}
}
}
- foregroundOps = which;
}
}
@@ -1562,33 +1538,24 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- final SparseArray<ArraySet<ModeCallback>> mOpModeWatchers = new SparseArray<>();
- final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
final ArrayMap<IBinder, SparseArray<StartedCallback>> mStartedWatchers = new ArrayMap<>();
final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager();
- final class ModeCallback implements DeathRecipient {
+ final class ModeCallback extends OnOpModeChangedListener implements DeathRecipient {
/** If mWatchedOpCode==ALL_OPS notify for ops affected by the switch-op */
public static final int ALL_OPS = -2;
- final IAppOpsCallback mCallback;
- final int mWatchingUid;
- final int mFlags;
- final int mWatchedOpCode;
- final int mCallingUid;
- final int mCallingPid;
+ // Need to keep this only because stopWatchingMode needs an IAppOpsCallback.
+ // Otherwise we can just use the IBinder object.
+ private final IAppOpsCallback mCallback;
- ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOp,
+ ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOpCode,
int callingUid, int callingPid) {
- mCallback = callback;
- mWatchingUid = watchingUid;
- mFlags = flags;
- mWatchedOpCode = watchedOp;
- mCallingUid = callingUid;
- mCallingPid = callingPid;
+ super(watchingUid, flags, watchedOpCode, callingUid, callingPid);
+ this.mCallback = callback;
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -1596,20 +1563,16 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- public boolean isWatchingUid(int uid) {
- return uid == UID_ANY || mWatchingUid < 0 || mWatchingUid == uid;
- }
-
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("ModeCallback{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" watchinguid=");
- UserHandle.formatUid(sb, mWatchingUid);
+ UserHandle.formatUid(sb, getWatchingUid());
sb.append(" flags=0x");
- sb.append(Integer.toHexString(mFlags));
- switch (mWatchedOpCode) {
+ sb.append(Integer.toHexString(getFlags()));
+ switch (getWatchedOpCode()) {
case OP_NONE:
break;
case ALL_OPS:
@@ -1617,13 +1580,13 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
break;
default:
sb.append(" op=");
- sb.append(opToName(mWatchedOpCode));
+ sb.append(opToName(getWatchedOpCode()));
break;
}
sb.append(" from uid=");
- UserHandle.formatUid(sb, mCallingUid);
+ UserHandle.formatUid(sb, getCallingUid());
sb.append(" pid=");
- sb.append(mCallingPid);
+ sb.append(getCallingPid());
sb.append('}');
return sb.toString();
}
@@ -1636,6 +1599,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public void binderDied() {
stopWatchingMode(mCallback);
}
+
+ @Override
+ public void onOpModeChanged(int op, int uid, String packageName) throws RemoteException {
+ mCallback.opChanged(op, uid, packageName);
+ }
}
final class ActiveCallback implements DeathRecipient {
@@ -1804,7 +1772,14 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public AppOpsService(File storagePath, Handler handler, Context context) {
mContext = context;
- mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this);
+
+ for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) {
+ int switchCode = AppOpsManager.opToSwitch(switchedCode);
+ mSwitchedOps.put(switchCode,
+ ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode));
+ }
+ mAppOpsServiceInterface =
+ new LegacyAppOpsServiceInterfaceImpl(this, this, handler, context, mSwitchedOps);
LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath, "appops");
@@ -1818,12 +1793,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
mHandler = handler;
mConstants = new Constants(mHandler);
readState();
-
- for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) {
- int switchCode = AppOpsManager.opToSwitch(switchedCode);
- mSwitchedOps.put(switchCode,
- ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode));
- }
}
public void publish() {
@@ -1982,20 +1951,20 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
final String[] changedPkgs = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
for (int code : OPS_RESTRICTED_ON_SUSPEND) {
- ArraySet<ModeCallback> callbacks;
+ ArraySet<OnOpModeChangedListener> onModeChangedListeners;
synchronized (AppOpsService.this) {
- callbacks = mOpModeWatchers.get(code);
- if (callbacks == null) {
+ onModeChangedListeners =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (onModeChangedListeners == null) {
continue;
}
- callbacks = new ArraySet<>(callbacks);
}
for (int i = 0; i < changedUids.length; i++) {
final int changedUid = changedUids[i];
final String changedPkg = changedPkgs[i];
// We trust packagemanager to insert matching uid and packageNames in the
// extras
- notifyOpChanged(callbacks, code, changedUid, changedPkg);
+ notifyOpChanged(onModeChangedListeners, code, changedUid, changedPkg);
}
}
}
@@ -2596,7 +2565,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
if (!uidState.setUidMode(code, mode)) {
return;
}
- uidState.evalForegroundOps(mOpModeWatchers);
+ uidState.evalForegroundOps();
if (mode != MODE_ERRORED && mode != previousMode) {
updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
}
@@ -2615,78 +2584,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
*/
private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground,
@Nullable IAppOpsCallback callbackToIgnore) {
- String[] uidPackageNames = getPackagesForUid(uid);
- ArrayMap<ModeCallback, ArraySet<String>> callbackSpecs = null;
-
- synchronized (this) {
- ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
- if (callbacks != null) {
- final int callbackCount = callbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- ModeCallback callback = callbacks.valueAt(i);
- if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) {
- continue;
- }
-
- ArraySet<String> changedPackages = new ArraySet<>();
- Collections.addAll(changedPackages, uidPackageNames);
- if (callbackSpecs == null) {
- callbackSpecs = new ArrayMap<>();
- }
- callbackSpecs.put(callback, changedPackages);
- }
- }
-
- for (String uidPackageName : uidPackageNames) {
- callbacks = mPackageModeWatchers.get(uidPackageName);
- if (callbacks != null) {
- if (callbackSpecs == null) {
- callbackSpecs = new ArrayMap<>();
- }
- final int callbackCount = callbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- ModeCallback callback = callbacks.valueAt(i);
- if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) {
- continue;
- }
-
- ArraySet<String> changedPackages = callbackSpecs.get(callback);
- if (changedPackages == null) {
- changedPackages = new ArraySet<>();
- callbackSpecs.put(callback, changedPackages);
- }
- changedPackages.add(uidPackageName);
- }
- }
- }
-
- if (callbackSpecs != null && callbackToIgnore != null) {
- callbackSpecs.remove(mModeWatchers.get(callbackToIgnore.asBinder()));
- }
- }
-
- if (callbackSpecs == null) {
- return;
- }
-
- for (int i = 0; i < callbackSpecs.size(); i++) {
- final ModeCallback callback = callbackSpecs.keyAt(i);
- final ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
- if (reportedPackageNames == null) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsService::notifyOpChanged,
- this, callback, code, uid, (String) null));
-
- } else {
- final int reportedPackageCount = reportedPackageNames.size();
- for (int j = 0; j < reportedPackageCount; j++) {
- final String reportedPackageName = reportedPackageNames.valueAt(j);
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsService::notifyOpChanged,
- this, callback, code, uid, reportedPackageName));
- }
- }
- }
+ ModeCallback listenerToIgnore = callbackToIgnore != null
+ ? mModeWatchers.get(callbackToIgnore.asBinder()) : null;
+ mAppOpsServiceInterface.notifyOpChangedForAllPkgsInUid(code, uid, onlyForeground,
+ listenerToIgnore);
}
private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
@@ -2810,7 +2711,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
return;
}
- ArraySet<ModeCallback> repCbs = null;
+ ArraySet<OnOpModeChangedListener> repCbs = null;
code = AppOpsManager.opToSwitch(code);
PackageVerificationResult pvr;
@@ -2831,16 +2732,17 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
op.setMode(mode);
if (uidState != null) {
- uidState.evalForegroundOps(mOpModeWatchers);
+ uidState.evalForegroundOps();
}
- ArraySet<ModeCallback> cbs = mOpModeWatchers.get(code);
+ ArraySet<OnOpModeChangedListener> cbs =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
if (cbs != null) {
if (repCbs == null) {
repCbs = new ArraySet<>();
}
repCbs.addAll(cbs);
}
- cbs = mPackageModeWatchers.get(packageName);
+ cbs = mAppOpsServiceInterface.getPackageModeChangedListeners(packageName);
if (cbs != null) {
if (repCbs == null) {
repCbs = new ArraySet<>();
@@ -2871,47 +2773,17 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
notifyOpChangedSync(code, uid, packageName, mode, previousMode);
}
- private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code,
+ private void notifyOpChanged(ArraySet<OnOpModeChangedListener> callbacks, int code,
int uid, String packageName) {
for (int i = 0; i < callbacks.size(); i++) {
- final ModeCallback callback = callbacks.valueAt(i);
+ final OnOpModeChangedListener callback = callbacks.valueAt(i);
notifyOpChanged(callback, code, uid, packageName);
}
}
- private void notifyOpChanged(ModeCallback callback, int code,
+ private void notifyOpChanged(OnOpModeChangedListener callback, int code,
int uid, String packageName) {
- if (uid != UID_ANY && callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
- return;
- }
-
- // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
- int[] switchedCodes;
- if (callback.mWatchedOpCode == ALL_OPS) {
- switchedCodes = mSwitchedOps.get(code);
- } else if (callback.mWatchedOpCode == OP_NONE) {
- switchedCodes = new int[]{code};
- } else {
- switchedCodes = new int[]{callback.mWatchedOpCode};
- }
-
- for (int switchedCode : switchedCodes) {
- // There are features watching for mode changes such as window manager
- // and location manager which are in our process. The callbacks in these
- // features may require permissions our remote caller does not have.
- final long identity = Binder.clearCallingIdentity();
- try {
- if (shouldIgnoreCallback(switchedCode, callback.mCallingPid,
- callback.mCallingUid)) {
- continue;
- }
- callback.mCallback.opChanged(switchedCode, uid, packageName);
- } catch (RemoteException e) {
- /* ignore */
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ mAppOpsServiceInterface.notifyOpChanged(callback, code, uid, packageName);
}
private static ArrayList<ChangeRec> addChange(ArrayList<ChangeRec> reports,
@@ -2936,9 +2808,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
return reports;
}
- private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks(
- HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks,
- int op, int uid, String packageName, int previousMode, ArraySet<ModeCallback> cbs) {
+ private static HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> addCallbacks(
+ HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> callbacks,
+ int op, int uid, String packageName, int previousMode,
+ ArraySet<OnOpModeChangedListener> cbs) {
if (cbs == null) {
return callbacks;
}
@@ -2947,7 +2820,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
final int N = cbs.size();
for (int i=0; i<N; i++) {
- ModeCallback cb = cbs.valueAt(i);
+ OnOpModeChangedListener cb = cbs.valueAt(i);
ArrayList<ChangeRec> reports = callbacks.get(cb);
ArrayList<ChangeRec> changed = addChange(reports, op, uid, packageName, previousMode);
if (changed != reports) {
@@ -2990,7 +2863,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
enforceManageAppOpsModes(callingPid, callingUid, reqUid);
- HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks = null;
+ HashMap<OnOpModeChangedListener, ArrayList<ChangeRec>> callbacks = null;
ArrayList<ChangeRec> allChanges = new ArrayList<>();
synchronized (this) {
boolean changed = false;
@@ -3007,9 +2880,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
- previousMode, mOpModeWatchers.get(code));
+ previousMode,
+ mAppOpsServiceInterface.getOpModeChangedListeners(code));
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
- previousMode, mPackageModeWatchers.get(packageName));
+ previousMode, mAppOpsServiceInterface
+ .getPackageModeChangedListeners(packageName));
allChanges = addChange(allChanges, code, uidState.uid,
packageName, previousMode);
@@ -3053,9 +2928,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
uidChanged = true;
final int uid = curOp.uidState.uid;
callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
- previousMode, mOpModeWatchers.get(curOp.op));
+ previousMode,
+ mAppOpsServiceInterface.getOpModeChangedListeners(curOp.op));
callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
- previousMode, mPackageModeWatchers.get(packageName));
+ previousMode, mAppOpsServiceInterface
+ .getPackageModeChangedListeners(packageName));
allChanges = addChange(allChanges, curOp.op, uid, packageName,
previousMode);
@@ -3075,7 +2952,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
mUidStates.remove(uidState.uid);
}
if (uidChanged) {
- uidState.evalForegroundOps(mOpModeWatchers);
+ uidState.evalForegroundOps();
}
}
@@ -3084,8 +2961,9 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
if (callbacks != null) {
- for (Map.Entry<ModeCallback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
- ModeCallback cb = ent.getKey();
+ for (Map.Entry<OnOpModeChangedListener, ArrayList<ChangeRec>> ent
+ : callbacks.entrySet()) {
+ OnOpModeChangedListener cb = ent.getKey();
ArrayList<ChangeRec> reports = ent.getValue();
for (int i=0; i<reports.size(); i++) {
ChangeRec rep = reports.get(i);
@@ -3121,7 +2999,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
for (int uidi = mUidStates.size() - 1; uidi >= 0; uidi--) {
final UidState uidState = mUidStates.valueAt(uidi);
if (uidState.foregroundOps != null) {
- uidState.evalForegroundOps(mOpModeWatchers);
+ uidState.evalForegroundOps();
}
}
}
@@ -3169,20 +3047,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
mModeWatchers.put(callback.asBinder(), cb);
}
if (switchOp != AppOpsManager.OP_NONE) {
- ArraySet<ModeCallback> cbs = mOpModeWatchers.get(switchOp);
- if (cbs == null) {
- cbs = new ArraySet<>();
- mOpModeWatchers.put(switchOp, cbs);
- }
- cbs.add(cb);
+ mAppOpsServiceInterface.startWatchingOpModeChanged(cb, switchOp);
}
if (mayWatchPackageName) {
- ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName);
- if (cbs == null) {
- cbs = new ArraySet<>();
- mPackageModeWatchers.put(packageName, cbs);
- }
- cbs.add(cb);
+ mAppOpsServiceInterface.startWatchingPackageModeChanged(cb, packageName);
}
evalAllForegroundOpsLocked();
}
@@ -3197,21 +3065,9 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
ModeCallback cb = mModeWatchers.remove(callback.asBinder());
if (cb != null) {
cb.unlinkToDeath();
- for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
- ArraySet<ModeCallback> cbs = mOpModeWatchers.valueAt(i);
- cbs.remove(cb);
- if (cbs.size() <= 0) {
- mOpModeWatchers.removeAt(i);
- }
- }
- for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
- ArraySet<ModeCallback> cbs = mPackageModeWatchers.valueAt(i);
- cbs.remove(cb);
- if (cbs.size() <= 0) {
- mPackageModeWatchers.removeAt(i);
- }
- }
+ mAppOpsServiceInterface.removeListener(cb);
}
+
evalAllForegroundOpsLocked();
}
}
@@ -4542,12 +4398,14 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
AppOpsService::notifyOpChangedForAllPkgsInUid,
this, code, uidState.uid, true, null));
} else if (uidState.pkgOps != null) {
- final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
- if (callbacks != null) {
- for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) {
- final ModeCallback callback = callbacks.valueAt(cbi);
- if ((callback.mFlags & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
- || !callback.isWatchingUid(uidState.uid)) {
+ final ArraySet<OnOpModeChangedListener> listenerSet =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (listenerSet != null) {
+ for (int cbi = listenerSet.size() - 1; cbi >= 0; cbi--) {
+ final OnOpModeChangedListener listener = listenerSet.valueAt(cbi);
+ if ((listener.getFlags()
+ & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
+ || !listener.isWatchingUid(uidState.uid)) {
continue;
}
for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
@@ -4558,7 +4416,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
- this, callback, code, uidState.uid,
+ this, listenerSet.valueAt(cbi), code, uidState.uid,
uidState.pkgOps.keyAt(pkgi)));
}
}
@@ -5045,7 +4903,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
if (changed) {
- uidState.evalForegroundOps(mOpModeWatchers);
+ uidState.evalForegroundOps();
}
}
}
@@ -5131,7 +4989,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
XmlUtils.skipCurrentTag(parser);
}
}
- uidState.evalForegroundOps(mOpModeWatchers);
+ uidState.evalForegroundOps();
}
private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent,
@@ -6122,62 +5980,17 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
pw.println();
}
- if (mOpModeWatchers.size() > 0 && !dumpHistory) {
- boolean printedHeader = false;
- for (int i=0; i<mOpModeWatchers.size(); i++) {
- if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) {
- continue;
- }
- boolean printedOpHeader = false;
- ArraySet<ModeCallback> callbacks = mOpModeWatchers.valueAt(i);
- for (int j=0; j<callbacks.size(); j++) {
- final ModeCallback cb = callbacks.valueAt(j);
- if (dumpPackage != null
- && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
- continue;
- }
- needSep = true;
- if (!printedHeader) {
- pw.println(" Op mode watchers:");
- printedHeader = true;
- }
- if (!printedOpHeader) {
- pw.print(" Op ");
- pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
- pw.println(":");
- printedOpHeader = true;
- }
- pw.print(" #"); pw.print(j); pw.print(": ");
- pw.println(cb);
- }
- }
- }
- if (mPackageModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) {
- boolean printedHeader = false;
- for (int i=0; i<mPackageModeWatchers.size(); i++) {
- if (dumpPackage != null && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) {
- continue;
- }
- needSep = true;
- if (!printedHeader) {
- pw.println(" Package mode watchers:");
- printedHeader = true;
- }
- pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
- pw.println(":");
- ArraySet<ModeCallback> callbacks = mPackageModeWatchers.valueAt(i);
- for (int j=0; j<callbacks.size(); j++) {
- pw.print(" #"); pw.print(j); pw.print(": ");
- pw.println(callbacks.valueAt(j));
- }
- }
+
+ if (!dumpHistory) {
+ needSep |= mAppOpsServiceInterface.dumpListeners(dumpOp, dumpUid, dumpPackage, pw);
}
+
if (mModeWatchers.size() > 0 && dumpOp < 0 && !dumpHistory) {
boolean printedHeader = false;
- for (int i=0; i<mModeWatchers.size(); i++) {
+ for (int i = 0; i < mModeWatchers.size(); i++) {
final ModeCallback cb = mModeWatchers.valueAt(i);
if (dumpPackage != null
- && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
+ && dumpUid != UserHandle.getAppId(cb.getWatchingUid())) {
continue;
}
needSep = true;
@@ -6729,16 +6542,15 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
private void notifyWatchersOfChange(int code, int uid) {
- final ArraySet<ModeCallback> clonedCallbacks;
+ final ArraySet<OnOpModeChangedListener> modeChangedListenerSet;
synchronized (this) {
- ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
- if (callbacks == null) {
+ modeChangedListenerSet = mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (modeChangedListenerSet == null) {
return;
}
- clonedCallbacks = new ArraySet<>(callbacks);
}
- notifyOpChanged(clonedCallbacks, code, uid, null);
+ notifyOpChanged(modeChangedListenerSet, code, uid, null);
}
@Override
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
index cd5ea120f878..c707086fb2fb 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -14,12 +14,20 @@
* limitations under the License.
*/
package com.android.server.appop;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager.Mode;
+import android.util.ArraySet;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+
+import java.io.PrintWriter;
+
/**
* Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
- * In the future this interface will also include mode callbacks and op restrictions.
+ * This interface also includes functions for added and removing op mode watchers.
+ * In the future this interface will also include op restrictions.
*/
public interface AppOpsServiceInterface {
/**
@@ -95,4 +103,93 @@ public interface AppOpsServiceInterface {
* Stop tracking app-op modes for all uid and packages.
*/
void clearAllModes();
+
+ /**
+ * Registers changedListener to listen to op's mode change.
+ * @param changedListener the listener that must be trigger on the op's mode change.
+ * @param op op representing the app-op whose mode change needs to be listened to.
+ */
+ void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op);
+
+ /**
+ * Registers changedListener to listen to package's app-op's mode change.
+ * @param changedListener the listener that must be trigger on the mode change.
+ * @param packageName of the package whose app-op's mode change needs to be listened to.
+ */
+ void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
+ @NonNull String packageName);
+
+ /**
+ * Stop the changedListener from triggering on any mode change.
+ * @param changedListener the listener that needs to be removed.
+ */
+ void removeListener(@NonNull OnOpModeChangedListener changedListener);
+
+ /**
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Returns a set of OnOpModeChangedListener that are listening for op's mode changes.
+ * @param op app-op whose mode change is being listened to.
+ */
+ ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op);
+
+ /**
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes.
+ * @param packageName of package whose app-op's mode change is being listened to.
+ */
+ ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName);
+
+ /**
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Notify that the app-op's mode is changed by triggering the change listener.
+ * @param changedListener the change listener.
+ * @param op App-op whose mode has changed
+ * @param uid user id associated with the app-op
+ * @param packageName package name that is associated with the app-op
+ */
+ void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
+ @Nullable String packageName);
+
+ /**
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Notify that the app-op's mode is changed to all packages associated with the uid by
+ * triggering the appropriate change listener.
+ * @param op App-op whose mode has changed
+ * @param uid user id associated with the app-op
+ * @param onlyForeground true if only watchers that
+ * @param callbackToIgnore callback that should be ignored.
+ */
+ void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
+ @Nullable OnOpModeChangedListener callbackToIgnore);
+
+ /**
+ * TODO: Move hasForegroundWatchers and foregroundOps into this.
+ * Go over the list of app-ops for the uid and mark app-ops with MODE_FOREGROUND in
+ * foregroundOps.
+ * @param uid for which the app-op's mode needs to be marked.
+ * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
+ * @return foregroundOps.
+ */
+ SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps);
+
+ /**
+ * Go over the list of app-ops for the package name and mark app-ops with MODE_FOREGROUND in
+ * foregroundOps.
+ * @param packageName for which the app-op's mode needs to be marked.
+ * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
+ * @return foregroundOps.
+ */
+ SparseBooleanArray evalForegroundPackageOps(String packageName,
+ SparseBooleanArray foregroundOps);
+
+ /**
+ * Dump op mode and package mode listeners and their details.
+ * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's set to an
+ * app-op, only the watchers for that app-op are dumped.
+ * @param dumpUid uid for which we want to dump op mode watchers.
+ * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package name.
+ * @param printWriter writer to dump to.
+ */
+ boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, PrintWriter printWriter);
+
}
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
index c27c0d3de5d7..2d498ea2117c 100644
--- a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -16,15 +16,39 @@
package com.android.server.appop;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
+import static android.app.AppOpsManager.opRestrictsRead;
+
+import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
+
+import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManager.Mode;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import libcore.util.EmptyArray;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Objects;
/**
@@ -33,8 +57,13 @@ import com.android.internal.annotations.VisibleForTesting;
*/
public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface {
- // Should be the same object that the AppOpsService is using for locking.
+ static final String TAG = "LegacyAppOpsServiceInterfaceImpl";
+
+ // Must be the same object that the AppOpsService is using for locking.
final Object mLock;
+ final Handler mHandler;
+ final Context mContext;
+ final SparseArray<int[]> mSwitchedOps;
@GuardedBy("mLock")
@VisibleForTesting
@@ -43,13 +72,25 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface
@GuardedBy("mLock")
final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>();
+ final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers = new SparseArray<>();
+ final ArrayMap<String, ArraySet<OnOpModeChangedListener>> mPackageModeWatchers =
+ new ArrayMap<>();
+
final PersistenceScheduler mPersistenceScheduler;
+ // Constant meaning that any UID should be matched when dispatching callbacks
+ private static final int UID_ANY = -2;
+
+
LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler,
- @NonNull Object lock) {
+ @NonNull Object lock, Handler handler, Context context,
+ SparseArray<int[]> switchedOps) {
this.mPersistenceScheduler = persistenceScheduler;
this.mLock = lock;
+ this.mHandler = handler;
+ this.mContext = context;
+ this.mSwitchedOps = switchedOps;
}
@Override
@@ -158,7 +199,6 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface
}
}
-
@Override
public boolean areUidModesDefault(int uid) {
synchronized (mLock) {
@@ -195,4 +235,335 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface
}
}
-}
+ @Override
+ public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener,
+ int op) {
+ Objects.requireNonNull(changedListener);
+ synchronized (mLock) {
+ ArraySet<OnOpModeChangedListener> modeWatcherSet = mOpModeWatchers.get(op);
+ if (modeWatcherSet == null) {
+ modeWatcherSet = new ArraySet<>();
+ mOpModeWatchers.put(op, modeWatcherSet);
+ }
+ modeWatcherSet.add(changedListener);
+ }
+ }
+
+ @Override
+ public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
+ @NonNull String packageName) {
+ Objects.requireNonNull(changedListener);
+ Objects.requireNonNull(packageName);
+ synchronized (mLock) {
+ ArraySet<OnOpModeChangedListener> modeWatcherSet =
+ mPackageModeWatchers.get(packageName);
+ if (modeWatcherSet == null) {
+ modeWatcherSet = new ArraySet<>();
+ mPackageModeWatchers.put(packageName, modeWatcherSet);
+ }
+ modeWatcherSet.add(changedListener);
+ }
+ }
+
+ @Override
+ public void removeListener(@NonNull OnOpModeChangedListener changedListener) {
+ Objects.requireNonNull(changedListener);
+
+ synchronized (mLock) {
+ for (int i = mOpModeWatchers.size() - 1; i >= 0; i--) {
+ ArraySet<OnOpModeChangedListener> cbs = mOpModeWatchers.valueAt(i);
+ cbs.remove(changedListener);
+ if (cbs.size() <= 0) {
+ mOpModeWatchers.removeAt(i);
+ }
+ }
+
+ for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) {
+ ArraySet<OnOpModeChangedListener> cbs = mPackageModeWatchers.valueAt(i);
+ cbs.remove(changedListener);
+ if (cbs.size() <= 0) {
+ mPackageModeWatchers.removeAt(i);
+ }
+ }
+ }
+ }
+
+ @Override
+ public ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op) {
+ synchronized (mLock) {
+ ArraySet<OnOpModeChangedListener> modeChangedListenersSet = mOpModeWatchers.get(op);
+ if (modeChangedListenersSet == null) {
+ return new ArraySet<>();
+ }
+ return new ArraySet<>(modeChangedListenersSet);
+ }
+ }
+
+ @Override
+ public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(
+ @NonNull String packageName) {
+ Objects.requireNonNull(packageName);
+
+ synchronized (mLock) {
+ ArraySet<OnOpModeChangedListener> modeChangedListenersSet =
+ mPackageModeWatchers.get(packageName);
+ if (modeChangedListenersSet == null) {
+ return new ArraySet<>();
+ }
+ return new ArraySet<>(modeChangedListenersSet);
+ }
+ }
+
+ @Override
+ public void notifyOpChanged(@NonNull OnOpModeChangedListener onModeChangedListener, int code,
+ int uid, @Nullable String packageName) {
+ Objects.requireNonNull(onModeChangedListener);
+
+ if (uid != UID_ANY && onModeChangedListener.getWatchingUid() >= 0
+ && onModeChangedListener.getWatchingUid() != uid) {
+ return;
+ }
+
+ // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
+ int[] switchedCodes;
+ if (onModeChangedListener.getWatchedOpCode() == ALL_OPS) {
+ switchedCodes = mSwitchedOps.get(code);
+ } else if (onModeChangedListener.getWatchedOpCode() == OP_NONE) {
+ switchedCodes = new int[]{code};
+ } else {
+ switchedCodes = new int[]{onModeChangedListener.getWatchedOpCode()};
+ }
+
+ for (int switchedCode : switchedCodes) {
+ // There are features watching for mode changes such as window manager
+ // and location manager which are in our process. The callbacks in these
+ // features may require permissions our remote caller does not have.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (shouldIgnoreCallback(switchedCode, onModeChangedListener.getCallingPid(),
+ onModeChangedListener.getCallingUid())) {
+ continue;
+ }
+ onModeChangedListener.onOpModeChanged(switchedCode, uid, packageName);
+ } catch (RemoteException e) {
+ /* ignore */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ private boolean shouldIgnoreCallback(int op, int watcherPid, int watcherUid) {
+ // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission,
+ // as watcher should not use this to signal if the value is changed.
+ return opRestrictsRead(op) && mContext.checkPermission(Manifest.permission.MANAGE_APPOPS,
+ watcherPid, watcherUid) != PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground,
+ @Nullable OnOpModeChangedListener callbackToIgnore) {
+ String[] uidPackageNames = getPackagesForUid(uid);
+ ArrayMap<OnOpModeChangedListener, ArraySet<String>> callbackSpecs = null;
+
+ synchronized (mLock) {
+ ArraySet<OnOpModeChangedListener> callbacks = mOpModeWatchers.get(code);
+ if (callbacks != null) {
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ OnOpModeChangedListener callback = callbacks.valueAt(i);
+
+ if (onlyForeground && (callback.getFlags()
+ & WATCH_FOREGROUND_CHANGES) == 0) {
+ continue;
+ }
+
+ ArraySet<String> changedPackages = new ArraySet<>();
+ Collections.addAll(changedPackages, uidPackageNames);
+ if (callbackSpecs == null) {
+ callbackSpecs = new ArrayMap<>();
+ }
+ callbackSpecs.put(callback, changedPackages);
+ }
+ }
+
+ for (String uidPackageName : uidPackageNames) {
+ callbacks = mPackageModeWatchers.get(uidPackageName);
+ if (callbacks != null) {
+ if (callbackSpecs == null) {
+ callbackSpecs = new ArrayMap<>();
+ }
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ OnOpModeChangedListener callback = callbacks.valueAt(i);
+
+ if (onlyForeground && (callback.getFlags()
+ & WATCH_FOREGROUND_CHANGES) == 0) {
+ continue;
+ }
+
+ ArraySet<String> changedPackages = callbackSpecs.get(callback);
+ if (changedPackages == null) {
+ changedPackages = new ArraySet<>();
+ callbackSpecs.put(callback, changedPackages);
+ }
+ changedPackages.add(uidPackageName);
+ }
+ }
+ }
+
+ if (callbackSpecs != null && callbackToIgnore != null) {
+ callbackSpecs.remove(callbackToIgnore);
+ }
+ }
+
+ if (callbackSpecs == null) {
+ return;
+ }
+
+ for (int i = 0; i < callbackSpecs.size(); i++) {
+ final OnOpModeChangedListener callback = callbackSpecs.keyAt(i);
+ final ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
+ if (reportedPackageNames == null) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LegacyAppOpsServiceInterfaceImpl::notifyOpChanged,
+ this, callback, code, uid, (String) null));
+
+ } else {
+ final int reportedPackageCount = reportedPackageNames.size();
+ for (int j = 0; j < reportedPackageCount; j++) {
+ final String reportedPackageName = reportedPackageNames.valueAt(j);
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LegacyAppOpsServiceInterfaceImpl::notifyOpChanged,
+ this, callback, code, uid, reportedPackageName));
+ }
+ }
+ }
+ }
+
+ private static String[] getPackagesForUid(int uid) {
+ String[] packageNames = null;
+
+ // Very early during boot the package manager is not yet or not yet fully started. At this
+ // time there are no packages yet.
+ if (AppGlobals.getPackageManager() != null) {
+ try {
+ packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
+ } catch (RemoteException e) {
+ /* ignore - local call */
+ }
+ }
+ if (packageNames == null) {
+ return EmptyArray.STRING;
+ }
+ return packageNames;
+ }
+
+ @Override
+ public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) {
+ synchronized (mLock) {
+ return evalForegroundOps(mUidModes.get(uid), foregroundOps);
+ }
+ }
+
+ @Override
+ public SparseBooleanArray evalForegroundPackageOps(String packageName,
+ SparseBooleanArray foregroundOps) {
+ synchronized (mLock) {
+ return evalForegroundOps(mPackageModes.get(packageName), foregroundOps);
+ }
+ }
+
+ private SparseBooleanArray evalForegroundOps(SparseIntArray opModes,
+ SparseBooleanArray foregroundOps) {
+ SparseBooleanArray tempForegroundOps = foregroundOps;
+ if (opModes != null) {
+ for (int i = opModes.size() - 1; i >= 0; i--) {
+ if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
+ if (tempForegroundOps == null) {
+ tempForegroundOps = new SparseBooleanArray();
+ }
+ evalForegroundWatchers(opModes.keyAt(i), tempForegroundOps);
+ }
+ }
+ }
+ return tempForegroundOps;
+ }
+
+ private void evalForegroundWatchers(int op, SparseBooleanArray foregroundOps) {
+ boolean curValue = foregroundOps.get(op, false);
+ ArraySet<OnOpModeChangedListener> listenerSet = mOpModeWatchers.get(op);
+ if (listenerSet != null) {
+ for (int cbi = listenerSet.size() - 1; !curValue && cbi >= 0; cbi--) {
+ if ((listenerSet.valueAt(cbi).getFlags()
+ & AppOpsManager.WATCH_FOREGROUND_CHANGES) != 0) {
+ curValue = true;
+ }
+ }
+ }
+ foregroundOps.put(op, curValue);
+ }
+
+ @Override
+ public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage,
+ PrintWriter printWriter) {
+ boolean needSep = false;
+ if (mOpModeWatchers.size() > 0) {
+ boolean printedHeader = false;
+ for (int i = 0; i < mOpModeWatchers.size(); i++) {
+ if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) {
+ continue;
+ }
+ boolean printedOpHeader = false;
+ ArraySet<OnOpModeChangedListener> modeChangedListenerSet =
+ mOpModeWatchers.valueAt(i);
+ for (int j = 0; j < modeChangedListenerSet.size(); j++) {
+ final OnOpModeChangedListener listener = modeChangedListenerSet.valueAt(j);
+ if (dumpPackage != null
+ && dumpUid != UserHandle.getAppId(listener.getWatchingUid())) {
+ continue;
+ }
+ needSep = true;
+ if (!printedHeader) {
+ printWriter.println(" Op mode watchers:");
+ printedHeader = true;
+ }
+ if (!printedOpHeader) {
+ printWriter.print(" Op ");
+ printWriter.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
+ printWriter.println(":");
+ printedOpHeader = true;
+ }
+ printWriter.print(" #"); printWriter.print(j); printWriter.print(": ");
+ printWriter.println(listener.toString());
+ }
+ }
+ }
+
+ if (mPackageModeWatchers.size() > 0 && dumpOp < 0) {
+ boolean printedHeader = false;
+ for (int i = 0; i < mPackageModeWatchers.size(); i++) {
+ if (dumpPackage != null
+ && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) {
+ continue;
+ }
+ needSep = true;
+ if (!printedHeader) {
+ printWriter.println(" Package mode watchers:");
+ printedHeader = true;
+ }
+ printWriter.print(" Pkg "); printWriter.print(mPackageModeWatchers.keyAt(i));
+ printWriter.println(":");
+ ArraySet<OnOpModeChangedListener> modeChangedListenerSet =
+ mPackageModeWatchers.valueAt(i);
+
+ for (int j = 0; j < modeChangedListenerSet.size(); j++) {
+ printWriter.print(" #"); printWriter.print(j); printWriter.print(": ");
+ printWriter.println(modeChangedListenerSet.valueAt(j).toString());
+ }
+ }
+ }
+ return needSep;
+ }
+
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/appop/OnOpModeChangedListener.java b/services/core/java/com/android/server/appop/OnOpModeChangedListener.java
new file mode 100644
index 000000000000..5ebe8119f046
--- /dev/null
+++ b/services/core/java/com/android/server/appop/OnOpModeChangedListener.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+import android.os.RemoteException;
+
+/**
+ * Listener for mode changes, encapsulates methods that should be triggered in the event of a mode
+ * change.
+ */
+abstract class OnOpModeChangedListener {
+
+ // Constant meaning that any UID should be matched when dispatching callbacks
+ private static final int UID_ANY = -2;
+
+ private int mWatchingUid;
+ private int mFlags;
+ private int mWatchedOpCode;
+ private int mCallingUid;
+ private int mCallingPid;
+
+ OnOpModeChangedListener(int watchingUid, int flags, int watchedOpCode, int callingUid,
+ int callingPid) {
+ this.mWatchingUid = watchingUid;
+ this.mFlags = flags;
+ this.mWatchedOpCode = watchedOpCode;
+ this.mCallingUid = callingUid;
+ this.mCallingPid = callingPid;
+ }
+
+ /**
+ * Returns the user id that is watching for the mode change.
+ */
+ public int getWatchingUid() {
+ return mWatchingUid;
+ }
+
+ /**
+ * Returns the flags associated with the mode change listener.
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Get the app-op whose mode change should trigger the callback.
+ */
+ public int getWatchedOpCode() {
+ return mWatchedOpCode;
+ }
+
+ /**
+ * Get the user-id that triggered the app-op mode change to be watched.
+ */
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ /**
+ * Get the process-id that triggered the app-op mode change to be watched.
+ */
+ public int getCallingPid() {
+ return mCallingPid;
+ }
+
+ /**
+ * returns true if the user id passed in the param is the one that is watching for op mode
+ * changed.
+ */
+ public boolean isWatchingUid(int uid) {
+ return uid == UID_ANY || mWatchingUid < 0 || mWatchingUid == uid;
+ }
+
+ /**
+ * Method that should be triggered when the app-op's mode is changed.
+ * @param op app-op whose mode-change is being listened to.
+ * @param uid user-is associated with the app-op.
+ * @param packageName package name associated with the app-op.
+ */
+ public abstract void onOpModeChanged(int op, int uid, String packageName)
+ throws RemoteException;
+
+ /**
+ * Return human readable string representing the listener.
+ */
+ public abstract String toString();
+
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index eba9c7a5f14e..e97d9c22620e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1771,8 +1771,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
return;
}
Log.w(TAG, "Communication client died");
- removeCommunicationRouteClient(client.getBinder(), true);
- onUpdateCommunicationRouteClient("onCommunicationRouteClientDied");
+ setCommunicationRouteForClient(client.getBinder(), client.getPid(), null,
+ BtHelper.SCO_MODE_UNDEFINED, "onCommunicationRouteClientDied");
}
/**
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index aedbe4eb945a..b7e817e15452 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -729,8 +729,11 @@ public class SpatializerHelper {
}
private boolean isDeviceCompatibleWithSpatializationModes(@NonNull AudioDeviceAttributes ada) {
+ // modeForDevice will be neither transaural or binaural for devices that do not support
+ // spatial audio. For instance mono devices like earpiece, speaker safe or sco must
+ // not be included.
final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(),
- /*default when type not found*/ SpatializationMode.SPATIALIZER_BINAURAL);
+ /*default when type not found*/ -1);
if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
|| (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
&& mTransauralSupported)) {
@@ -1538,8 +1541,8 @@ public class SpatializerHelper {
@Override
public String toString() {
- return "type:" + mDeviceType + " addr:" + mDeviceAddress + " enabled:" + mEnabled
- + " HT:" + mHasHeadTracker + " HTenabled:" + mHeadTrackerEnabled;
+ return "type: " + mDeviceType + " addr: " + mDeviceAddress + " enabled: " + mEnabled
+ + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
}
String toPersistableString() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
new file mode 100644
index 000000000000..0f1fe68ad1d7
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Common attributes for all biometric service providers.
+ *
+ * @param <T> Internal settings type.
+ */
+public interface BiometricServiceProvider<T extends SensorPropertiesInternal> {
+
+ /** Checks if the specified sensor is owned by this provider. */
+ boolean containsSensor(int sensorId);
+
+ /** All sensor properties. */
+ @NonNull
+ List<T> getSensorProperties();
+
+ /** Properties for the given sensor id. */
+ @NonNull
+ T getSensorProperties(int sensorId);
+
+ boolean isHardwareDetected(int sensorId);
+
+ /** If the user has any enrollments for the given sensor. */
+ boolean hasEnrollments(int sensorId, int userId);
+
+ long getAuthenticatorId(int sensorId, int userId);
+
+ @LockoutTracker.LockoutMode
+ int getLockoutModeForUser(int sensorId, int userId);
+
+ void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+ boolean clearSchedulerBuffer);
+
+ void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
+
+ void dumpInternal(int sensorId, @NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
new file mode 100644
index 000000000000..4779f6f931ee
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.os.Handler;
+import android.os.IInterface;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Container for all BiometricServiceProvider implementations.
+ *
+ * @param <T> The service provider type.
+ * @param <P> The internal properties type.
+ * @param <C> The registration callback for {@link #invokeRegisteredCallback(IInterface, List)}.
+ */
+public abstract class BiometricServiceRegistry<T extends BiometricServiceProvider<P>,
+ P extends SensorPropertiesInternal,
+ C extends IInterface> {
+
+ private static final String TAG = "BiometricServiceRegistry";
+
+ // Volatile so they can be read without a lock once all services are registered.
+ // But, ideally remove this and provide immutable copies via the callback instead.
+ @Nullable
+ private volatile List<T> mServiceProviders;
+ @Nullable
+ private volatile List<P> mAllProps;
+
+ @NonNull
+ private final Supplier<IBiometricService> mBiometricServiceSupplier;
+ @NonNull
+ private final RemoteCallbackList<C> mRegisteredCallbacks = new RemoteCallbackList<>();
+
+ public BiometricServiceRegistry(@NonNull Supplier<IBiometricService> biometricSupplier) {
+ mBiometricServiceSupplier = biometricSupplier;
+ }
+
+ /**
+ * Register an implementation by creating a new authenticator and initializing it via
+ * {@link IBiometricService#registerAuthenticator(int, int, int, IBiometricAuthenticator)}
+ * using the given properties.
+ *
+ * @param service service to register with
+ * @param props internal properties to initialize the authenticator
+ */
+ protected abstract void registerService(@NonNull IBiometricService service, @NonNull P props);
+
+ /**
+ * Invoke the callback to notify clients that all authenticators have been registered.
+ *
+ * @param callback callback to invoke
+ * @param allProps properties of all authenticators
+ */
+ protected abstract void invokeRegisteredCallback(@NonNull C callback,
+ @NonNull List<P> allProps) throws RemoteException;
+
+ /**
+ * Register all authenticators in a background thread.
+ *
+ * @param serviceProvider Supplier function that will be invoked on the background thread.
+ */
+ public void registerAll(Supplier<List<T>> serviceProvider) {
+ // Some HAL might not be started before the system service and will cause the code below
+ // to wait, and some of the operations below might take a significant amount of time to
+ // complete (calls to the HALs). To avoid blocking the rest of system server we put
+ // this on a background thread.
+ final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+ true /* allowIo */);
+ thread.start();
+ final Handler handler = new Handler(thread.getLooper());
+ handler.post(() -> registerAllInBackground(serviceProvider));
+ thread.quitSafely();
+ }
+
+ /** Register authenticators now, only called by {@link #registerAll(Supplier).} */
+ @VisibleForTesting
+ public void registerAllInBackground(Supplier<List<T>> serviceProvider) {
+ List<T> providers = serviceProvider.get();
+ if (providers == null) {
+ providers = new ArrayList<>();
+ }
+
+ final IBiometricService biometricService = mBiometricServiceSupplier.get();
+ if (biometricService == null) {
+ throw new IllegalStateException("biometric service cannot be null");
+ }
+
+ // Register each sensor individually with BiometricService
+ final List<P> allProps = new ArrayList<>();
+ for (T provider : providers) {
+ final List<P> props = provider.getSensorProperties();
+ for (P prop : props) {
+ registerService(biometricService, prop);
+ }
+ allProps.addAll(props);
+ }
+
+ finishRegistration(providers, allProps);
+ }
+
+ private synchronized void finishRegistration(
+ @NonNull List<T> providers, @NonNull List<P> allProps) {
+ mServiceProviders = Collections.unmodifiableList(providers);
+ mAllProps = Collections.unmodifiableList(allProps);
+ broadcastAllAuthenticatorsRegistered();
+ }
+
+ /**
+ * Add a callback that will be invoked once the work from {@link #registerAll(Supplier)}
+ * has finished registering all providers (executes immediately if already done).
+ *
+ * @param callback registration callback
+ */
+ public synchronized void addAllRegisteredCallback(@Nullable C callback) {
+ if (callback == null) {
+ Slog.e(TAG, "addAllRegisteredCallback, callback is null");
+ return;
+ }
+
+ final boolean registered = mRegisteredCallbacks.register(callback);
+ final boolean allRegistered = mServiceProviders != null;
+ if (registered && allRegistered) {
+ broadcastAllAuthenticatorsRegistered();
+ } else if (!registered) {
+ Slog.e(TAG, "addAllRegisteredCallback failed to register callback");
+ }
+ }
+
+ private synchronized void broadcastAllAuthenticatorsRegistered() {
+ final int n = mRegisteredCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ final C cb = mRegisteredCallbacks.getBroadcastItem(i);
+ try {
+ invokeRegisteredCallback(cb, mAllProps);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in broadcastAllAuthenticatorsRegistered", e);
+ } finally {
+ mRegisteredCallbacks.unregister(cb);
+ }
+ }
+ mRegisteredCallbacks.finishBroadcast();
+ }
+
+ /**
+ * Get a list of registered providers.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @NonNull
+ public List<T> getProviders() {
+ return mServiceProviders != null ? mServiceProviders : Collections.emptyList();
+ }
+
+ /**
+ * Gets the provider for given sensor id or null if not registered.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @Nullable
+ public T getProviderForSensor(int sensorId) {
+ if (mServiceProviders != null) {
+ for (T provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return provider;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds the provider for devices with only a single sensor.
+ *
+ * If no providers returns null. If multiple sensors are present this method
+ * will return the first one that is found (this is a legacy for test devices that
+ * use aidl/hidl concurrently and should not occur on real devices).
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @Nullable
+ public Pair<Integer, T> getSingleProvider() {
+ if (mAllProps == null || mAllProps.isEmpty()) {
+ Slog.e(TAG, "No sensors found");
+ return null;
+ }
+
+ if (mAllProps.size() > 1) {
+ Slog.e(TAG, "getSingleProvider() called but multiple sensors present: "
+ + mAllProps.size());
+ }
+
+ final int sensorId = mAllProps.get(0).sensorId;
+ final T provider = getProviderForSensor(sensorId);
+ if (provider != null) {
+ return new Pair<>(sensorId, provider);
+ }
+
+ Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found");
+ return null;
+ }
+
+ /**
+ * Get the properties for all providers.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @NonNull
+ public List<P> getAllProperties() {
+ return mAllProps != null ? mAllProps : Collections.emptyList();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index 0d789f7a1840..f8543162f95e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -23,32 +23,64 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE;
import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.IBiometricStateListener;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.RemoteException;
+import android.os.UserManager;
import android.util.Slog;
import com.android.server.biometrics.Utils;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A callback for receiving notifications about biometric sensor state changes.
+ *
+ * @param <T> service provider type
+ * @param <P> internal property type
*/
-public class BiometricStateCallback implements ClientMonitorCallback {
+public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
+ P extends SensorPropertiesInternal> implements ClientMonitorCallback {
private static final String TAG = "BiometricStateCallback";
@NonNull
- private final CopyOnWriteArrayList<IBiometricStateListener>
- mBiometricStateListeners = new CopyOnWriteArrayList<>();
-
- private @BiometricStateListener.State int mBiometricState;
+ private final CopyOnWriteArrayList<IBiometricStateListener> mBiometricStateListeners =
+ new CopyOnWriteArrayList<>();
+ @NonNull
+ private final UserManager mUserManager;
+ @BiometricStateListener.State
+ private int mBiometricState;
+ @NonNull
+ private List<T> mProviders = List.of();
- public BiometricStateCallback() {
+ /**
+ * Create a new callback that must be {@link #start(List)}ed.
+ *
+ * @param userManager user manager
+ */
+ public BiometricStateCallback(@NonNull UserManager userManager) {
mBiometricState = STATE_IDLE;
+ mUserManager = userManager;
+ }
+
+ /**
+ * This should be called when the service has been initialized and all providers are ready.
+ *
+ * @param allProviders all registered biometric service providers
+ */
+ public synchronized void start(@NonNull List<T> allProviders) {
+ mProviders = Collections.unmodifiableList(allProviders);
+ broadcastCurrentEnrollmentState(null /* listener */);
}
+ /** Get the current state. */
+ @BiometricStateListener.State
public int getBiometricState() {
return mBiometricState;
}
@@ -120,23 +152,43 @@ public class BiometricStateCallback implements ClientMonitorCallback {
}
/**
- * This should be invoked when:
- * 1) Enrolled --> None-enrolled
- * 2) None-enrolled --> enrolled
- * 3) HAL becomes ready
- * 4) Listener is registered
+ * Enables clients to register a BiometricStateListener. For example, this is used to forward
+ * fingerprint sensor state changes to SideFpsEventHandler.
+ *
+ * @param listener listener to register
*/
- public void notifyAllEnrollmentStateChanged(int userId, int sensorId,
+ public synchronized void registerBiometricStateListener(
+ @NonNull IBiometricStateListener listener) {
+ mBiometricStateListeners.add(listener);
+ broadcastCurrentEnrollmentState(listener);
+ }
+
+ private synchronized void broadcastCurrentEnrollmentState(
+ @Nullable IBiometricStateListener listener) {
+ for (T provider : mProviders) {
+ for (SensorPropertiesInternal prop : provider.getSensorProperties()) {
+ for (UserInfo userInfo : mUserManager.getAliveUsers()) {
+ final boolean enrolled = provider.hasEnrollments(prop.sensorId, userInfo.id);
+ if (listener != null) {
+ notifyEnrollmentStateChanged(
+ listener, userInfo.id, prop.sensorId, enrolled);
+ } else {
+ notifyAllEnrollmentStateChanged(
+ userInfo.id, prop.sensorId, enrolled);
+ }
+ }
+ }
+ }
+ }
+
+ private void notifyAllEnrollmentStateChanged(int userId, int sensorId,
boolean hasEnrollments) {
for (IBiometricStateListener listener : mBiometricStateListeners) {
notifyEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments);
}
}
- /**
- * Notifies the listener of enrollment state changes.
- */
- public void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener,
+ private void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener,
int userId, int sensorId, boolean hasEnrollments) {
try {
listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments);
@@ -144,14 +196,4 @@ public class BiometricStateCallback implements ClientMonitorCallback {
Slog.e(TAG, "Remote exception", e);
}
}
-
- /**
- * Enables clients to register a BiometricStateListener. For example, this is used to forward
- * fingerprint sensor state changes to SideFpsEventHandler.
- *
- * @param listener
- */
- public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
- mBiometricStateListeners.add(listener);
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 79e65cc6d2e5..271bce9890c6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -17,20 +17,18 @@
package com.android.server.biometrics.sensors.face;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FACE;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricStateListener;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
@@ -39,18 +37,18 @@ import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceServiceReceiver;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.NativeHandle;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -58,10 +56,10 @@ import android.view.Surface;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -88,51 +86,10 @@ public class FaceService extends SystemService {
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull
- private final List<ServiceProvider> mServiceProviders;
-
- @Nullable
- private ServiceProvider getProviderForSensor(int sensorId) {
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return provider;
- }
- }
- return null;
- }
-
- /**
- * For devices with only a single provider, returns that provider. If no providers, or multiple
- * providers exist, returns null.
- */
- @Nullable
- private Pair<Integer, ServiceProvider> getSingleProvider() {
- final List<FaceSensorPropertiesInternal> properties = getSensorProperties();
- if (properties.size() != 1) {
- Slog.e(TAG, "Multiple sensors found: " + properties.size());
- return null;
- }
-
- // Theoretically we can just return the first provider, but maybe this is easier to
- // understand.
- final int sensorId = properties.get(0).sensorId;
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return new Pair<>(sensorId, provider);
- }
- }
-
- Slog.e(TAG, "Single sensor, but provider not found");
- return null;
- }
-
+ private final FaceServiceRegistry mRegistry;
@NonNull
- private List<FaceSensorPropertiesInternal> getSensorProperties() {
- final List<FaceSensorPropertiesInternal> properties = new ArrayList<>();
- for (ServiceProvider provider : mServiceProviders) {
- properties.addAll(provider.getSensorProperties());
- }
- return properties;
- }
+ private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal>
+ mBiometricStateCallback;
/**
* Receives the incoming binder calls from FaceManager.
@@ -142,8 +99,7 @@ public class FaceService extends SystemService {
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId);
@@ -156,9 +112,8 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
-
final ProtoOutputStream proto = new ProtoOutputStream();
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider != null) {
provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
@@ -170,16 +125,14 @@ public class FaceService extends SystemService {
@Override // Binder call
public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {
-
- return FaceService.this.getSensorProperties();
+ return mRegistry.getAllProperties();
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public FaceSensorPropertiesInternal getSensorProperties(int sensorId,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId
+ ", caller: " + opPackageName);
@@ -193,8 +146,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void generateChallenge(IBinder token, int sensorId, int userId,
IFaceServiceReceiver receiver, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
return;
@@ -207,8 +159,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
long challenge) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
return;
@@ -222,8 +173,7 @@ public class FaceService extends SystemService {
public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
return -1;
@@ -245,8 +195,7 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
@Override // Binder call
public void cancelEnrollment(final IBinder token, long requestId) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelEnrollment");
return;
@@ -260,7 +209,6 @@ public class FaceService extends SystemService {
public long authenticate(final IBinder token, final long operationId, int userId,
final IFaceServiceReceiver receiver, final String opPackageName,
boolean isKeyguardBypassEnabled) {
-
// TODO(b/152413782): If the sensor supports face detect and the device is encrypted or
// lockdown, something wrong happened. See similar path in FingerprintService.
@@ -273,7 +221,7 @@ public class FaceService extends SystemService {
// permission.
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
return -1;
@@ -301,7 +249,7 @@ public class FaceService extends SystemService {
return -1;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFace");
return -1;
@@ -318,8 +266,7 @@ public class FaceService extends SystemService {
IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
int cookie, boolean allowBackgroundAuthentication) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for prepareForAuthentication");
return;
@@ -336,8 +283,7 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public void startPreparedClient(int sensorId, int cookie) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for startPreparedClient");
return;
@@ -350,8 +296,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName,
final long requestId) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthentication");
return;
@@ -370,7 +315,7 @@ public class FaceService extends SystemService {
return;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelFaceDetect");
return;
@@ -383,8 +328,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void cancelAuthenticationFromService(int sensorId, final IBinder token,
final String opPackageName, final long requestId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthenticationFromService");
return;
@@ -397,8 +341,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void remove(final IBinder token, final int faceId, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for remove");
return;
@@ -412,7 +355,6 @@ public class FaceService extends SystemService {
@Override // Binder call
public void removeAll(final IBinder token, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
-
final FaceServiceReceiver internalReceiver = new FaceServiceReceiver() {
int sensorsFinishedRemoving = 0;
final int numSensors = getSensorPropertiesInternal(
@@ -432,7 +374,7 @@ public class FaceService extends SystemService {
// This effectively iterates through all sensors, but has to do so by finding all
// sensors under each provider.
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
for (FaceSensorPropertiesInternal prop : props) {
provider.scheduleRemoveAll(prop.sensorId, token, userId, internalReceiver,
@@ -467,27 +409,27 @@ public class FaceService extends SystemService {
try {
if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpProtoState(props.sensorId, proto, false);
}
}
proto.flush();
} else if (args.length > 0 && "--proto".equals(args[0])) {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpProtoMetrics(props.sensorId, fd);
}
}
} else if (args.length > 1 && "--hal".equals(args[0])) {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpHal(props.sensorId, fd,
Arrays.copyOfRange(args, 1, args.length, args.getClass()));
}
}
} else {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
pw.println("Dumping for sensorId: " + props.sensorId
+ ", provider: " + provider.getClass().getSimpleName());
@@ -504,10 +446,9 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public boolean isHardwareDetected(int sensorId, String opPackageName) {
-
final long token = Binder.clearCallingIdentity();
try {
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName);
return false;
@@ -521,12 +462,11 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public List<Face> getEnrolledFaces(int sensorId, int userId, String opPackageName) {
-
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName);
return Collections.emptyList();
@@ -538,12 +478,11 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName) {
-
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName);
return false;
@@ -555,8 +494,7 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getLockoutModeForUser");
return LockoutTracker.LOCKOUT_NONE;
@@ -569,8 +507,7 @@ public class FaceService extends SystemService {
@Override
public void invalidateAuthenticatorId(int sensorId, int userId,
IInvalidationCallback callback) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
return;
@@ -582,7 +519,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getAuthenticatorId");
return 0;
@@ -595,8 +532,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken,
String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
@@ -610,8 +546,7 @@ public class FaceService extends SystemService {
public void setFeature(final IBinder token, int userId, int feature, boolean enabled,
final byte[] hardwareAuthToken, IFaceServiceReceiver receiver,
final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for setFeature");
return;
@@ -625,8 +560,7 @@ public class FaceService extends SystemService {
@Override
public void getFeature(final IBinder token, int userId, int feature,
IFaceServiceReceiver receiver, final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for getFeature");
return;
@@ -636,18 +570,14 @@ public class FaceService extends SystemService {
new ClientMonitorCallbackConverter(receiver), opPackageName);
}
- private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
- for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
- mServiceProviders.add(
- Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
- }
- }
+ private List<ServiceProvider> getAidlProviders() {
+ final List<ServiceProvider> providers = new ArrayList<>();
- private void addAidlProviders() {
final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
if (instances == null || instances.length == 0) {
- return;
+ return providers;
}
+
for (String instance : instances) {
final String fqName = IFace.DESCRIPTOR + "/" + instance;
final IFace face = IFace.Stub.asInterface(
@@ -660,53 +590,41 @@ public class FaceService extends SystemService {
final SensorProps[] props = face.getSensorProps();
final FaceProvider provider = new FaceProvider(getContext(), props, instance,
mLockoutResetDispatcher, BiometricContext.getInstance(getContext()));
- mServiceProviders.add(provider);
+ providers.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
}
+
+ return providers;
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public void registerAuthenticators(
@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
-
- // Some HAL might not be started before the system service and will cause the code below
- // to wait, and some of the operations below might take a significant amount of time to
- // complete (calls to the HALs). To avoid blocking the rest of system server we put
- // this on a background thread.
- final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
- true /* allowIo */);
- thread.start();
- final Handler handler = new Handler(thread.getLooper());
-
- handler.post(() -> {
- addHidlProviders(hidlSensors);
- addAidlProviders();
-
- final IBiometricService biometricService = IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE));
-
- // Register each sensor individually with BiometricService
- for (ServiceProvider provider : mServiceProviders) {
- final List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
- for (FaceSensorPropertiesInternal prop : props) {
- final int sensorId = prop.sensorId;
- final @BiometricManager.Authenticators.Types int strength =
- Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength);
- final FaceAuthenticator authenticator = new FaceAuthenticator(
- mServiceWrapper, sensorId);
- try {
- biometricService.registerAuthenticator(sensorId, TYPE_FACE, strength,
- authenticator);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId);
- }
- }
+ mRegistry.registerAll(() -> {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
+ providers.add(
+ Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
}
+ providers.addAll(getAidlProviders());
+ return providers;
});
}
+
+ @Override
+ public void addAuthenticatorsRegisteredCallback(
+ IFaceAuthenticatorsRegisteredCallback callback) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mRegistry.addAllRegisteredCallback(callback);
+ }
+
+ @Override
+ public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
+ mBiometricStateCallback.registerBiometricStateListener(listener);
+ }
}
public FaceService(Context context) {
@@ -714,7 +632,16 @@ public class FaceService extends SystemService {
mServiceWrapper = new FaceServiceWrapper();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
- mServiceProviders = new ArrayList<>();
+ mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
+ mRegistry = new FaceServiceRegistry(mServiceWrapper,
+ () -> IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
+ mBiometricStateCallback.start(mRegistry.getProviders());
+ }
+ });
}
@Override
@@ -752,7 +679,7 @@ public class FaceService extends SystemService {
if (Utils.isVirtualEnabled(getContext())) {
Slog.i(TAG, "Sync virtual enrollments");
final int userId = ActivityManager.getCurrentUser();
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */,
true /* favorHalEnrollments */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
new file mode 100644
index 000000000000..0f0a81d24473
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.face.IFaceService;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BiometricServiceRegistry;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Registry for {@link IFaceService} providers. */
+public class FaceServiceRegistry extends BiometricServiceRegistry<ServiceProvider,
+ FaceSensorPropertiesInternal, IFaceAuthenticatorsRegisteredCallback> {
+
+ private static final String TAG = "FaceServiceRegistry";
+
+ @NonNull
+ private final IFaceService mService;
+
+ /** Creates a new registry tied to the given service. */
+ public FaceServiceRegistry(@NonNull IFaceService service,
+ @Nullable Supplier<IBiometricService> biometricSupplier) {
+ super(biometricSupplier);
+ mService = service;
+ }
+
+ @Override
+ protected void registerService(@NonNull IBiometricService service,
+ @NonNull FaceSensorPropertiesInternal props) {
+ @BiometricManager.Authenticators.Types final int strength =
+ Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
+ try {
+ service.registerAuthenticator(props.sensorId, TYPE_FACE, strength,
+ new FaceAuthenticator(mService, props.sensorId));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId);
+ }
+ }
+
+ @Override
+ protected void invokeRegisteredCallback(@NonNull IFaceAuthenticatorsRegisteredCallback callback,
+ @NonNull List<FaceSensorPropertiesInternal> allProps) throws RemoteException {
+ callback.onAllAuthenticatorsRegistered(allProps);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 6f98365332e2..4efaedbd5530 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -26,15 +26,13 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.server.biometrics.sensors.BiometricServiceProvider;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutTracker;
import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.List;
/**
@@ -56,24 +54,11 @@ import java.util.List;
* to check (e.g. via {@link FaceManager#getSensorPropertiesInternal()}) that the code path isn't
* taken. ServiceProviders will provide a no-op for unsupported operations to fail safely.
*/
-public interface ServiceProvider {
- /**
- * Checks if the specified sensor is owned by this provider.
- */
- boolean containsSensor(int sensorId);
-
- @NonNull
- List<FaceSensorPropertiesInternal> getSensorProperties();
-
- @NonNull
- FaceSensorPropertiesInternal getSensorProperties(int sensorId);
+public interface ServiceProvider extends BiometricServiceProvider<FaceSensorPropertiesInternal> {
@NonNull
List<Face> getEnrolledFaces(int sensorId, int userId);
- @LockoutTracker.LockoutMode
- int getLockoutModeForUser(int sensorId, int userId);
-
/**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to be
* invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
@@ -84,10 +69,6 @@ public interface ServiceProvider {
+ " this method");
}
- long getAuthenticatorId(int sensorId, int userId);
-
- boolean isHardwareDetected(int sensorId);
-
void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, String opPackageName);
@@ -142,13 +123,6 @@ public interface ServiceProvider {
void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments);
- void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
- boolean clearSchedulerBuffer);
-
- void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
-
- void dumpInternal(int sensorId, @NonNull PrintWriter pw);
-
@NonNull
ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 19d54c84c706..6bff179e8eb7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -285,6 +285,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFaces(sensorId, userId).isEmpty();
+ }
+
+ @Override
public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 65289122747c..c0a119ff5f1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -484,6 +484,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFaces(sensorId, userId).isEmpty();
+ }
+
+ @Override
@LockoutTracker.LockoutMode
public int getLockoutModeForUser(int sensorId, int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 2ba449ae089e..7e2742edd47a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -17,14 +17,11 @@
package com.android.server.biometrics.sensors.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
@@ -36,8 +33,6 @@ import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -65,7 +60,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -79,11 +73,9 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
@@ -115,74 +107,32 @@ public class FingerprintService extends SystemService {
protected static final String TAG = "FingerprintService";
- private final Object mLock = new Object();
private final AppOpsManager mAppOps;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
- @NonNull private final List<ServiceProvider> mServiceProviders;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull private final Handler mHandler;
- @NonNull private final BiometricContext mBiometricContext;
- @NonNull private final Supplier<IBiometricService> mBiometricServiceSupplier;
- @NonNull private final Function<String, IFingerprint> mIFingerprintProvider;
-
- @GuardedBy("mLock")
- @NonNull private final RemoteCallbackList<IFingerprintAuthenticatorsRegisteredCallback>
- mAuthenticatorsRegisteredCallbacks;
-
- @GuardedBy("mLock")
- @NonNull private final List<FingerprintSensorPropertiesInternal> mSensorProps;
-
- /**
- * Registers BiometricStateListener in list stored by FingerprintService
- * @param listener new BiometricStateListener being added
- */
- public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
- mBiometricStateCallback.registerBiometricStateListener(listener);
- broadcastCurrentEnrollmentState(listener);
- }
-
- /**
- * @param listener if non-null, notifies only this listener. if null, notifies all listeners
- * in {@link BiometricStateCallback}. This is slightly ugly, but reduces
- * redundant code.
- */
- private void broadcastCurrentEnrollmentState(@Nullable IBiometricStateListener listener) {
- final UserManager um = UserManager.get(getContext());
- synchronized (mLock) {
- // Update the new listener with current state of all sensors
- for (FingerprintSensorPropertiesInternal prop : mSensorProps) {
- final ServiceProvider provider = getProviderForSensor(prop.sensorId);
- for (UserInfo userInfo : um.getAliveUsers()) {
- final boolean enrolled = !provider
- .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty();
-
- // Defer this work and allow the loop to release the lock sooner
- mHandler.post(() -> {
- if (listener != null) {
- mBiometricStateCallback.notifyEnrollmentStateChanged(
- listener, userInfo.id, prop.sensorId, enrolled);
- } else {
- mBiometricStateCallback.notifyAllEnrollmentStateChanged(
- userInfo.id, prop.sensorId, enrolled);
- }
- });
- }
- }
- }
- }
+ @NonNull
+ private final BiometricContext mBiometricContext;
+ @NonNull
+ private final Supplier<String[]> mAidlInstanceNameSupplier;
+ @NonNull
+ private final Function<String, IFingerprint> mIFingerprintProvider;
+ @NonNull
+ private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
+ mBiometricStateCallback;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final FingerprintServiceRegistry mRegistry;
- /**
- * Receives the incoming binder calls from FingerprintManager.
- */
- private final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
+ /** Receives the incoming binder calls from FingerprintManager. */
+ @VisibleForTesting
+ final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId);
@@ -195,9 +145,8 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
-
final ProtoOutputStream proto = new ProtoOutputStream();
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider != null) {
provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
@@ -212,16 +161,14 @@ public class FingerprintService extends SystemService {
!= PackageManager.PERMISSION_GRANTED) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
-
- return FingerprintService.this.getSensorProperties();
+ return mRegistry.getAllProperties();
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId
+ ", caller: " + opPackageName);
@@ -234,8 +181,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void generateChallenge(IBinder token, int sensorId, int userId,
IFingerprintServiceReceiver receiver, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
return;
@@ -248,8 +194,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
long challenge) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
return;
@@ -264,8 +209,7 @@ public class FingerprintService extends SystemService {
public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
return -1;
@@ -278,8 +222,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT)
@Override // Binder call
public void cancelEnrollment(final IBinder token, long requestId) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelEnrollment");
return;
@@ -339,10 +282,10 @@ public class FingerprintService extends SystemService {
final Pair<Integer, ServiceProvider> provider;
if (sensorId == FingerprintManager.SENSOR_ID_ANY) {
- provider = getSingleProvider();
+ provider = mRegistry.getSingleProvider();
} else {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- provider = new Pair<>(sensorId, getProviderForSensor(sensorId));
+ provider = new Pair<>(sensorId, mRegistry.getProviderForSensor(sensorId));
}
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
@@ -374,7 +317,6 @@ public class FingerprintService extends SystemService {
final IFingerprintServiceReceiver receiver,
final String opPackageName,
boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException {
-
final Context context = getUiContext();
final Context promptContext = context.createPackageContextAsUser(
opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(uId));
@@ -468,7 +410,7 @@ public class FingerprintService extends SystemService {
return -1;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFingerprint");
return -1;
@@ -484,8 +426,7 @@ public class FingerprintService extends SystemService {
public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
long requestId, int cookie, boolean allowBackgroundAuthentication) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for prepareForAuthentication");
return;
@@ -501,8 +442,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
@Override // Binder call
public void startPreparedClient(int sensorId, int cookie) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for startPreparedClient");
return;
@@ -532,7 +472,7 @@ public class FingerprintService extends SystemService {
return;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthentication");
return;
@@ -553,7 +493,7 @@ public class FingerprintService extends SystemService {
// For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as
// cancelling authentication.
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelFingerprintDetect");
return;
@@ -566,11 +506,9 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
final String opPackageName, final long requestId) {
-
-
Slog.d(TAG, "cancelAuthenticationFromService, sensorId: " + sensorId);
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthenticationFromService");
return;
@@ -583,8 +521,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void remove(final IBinder token, final int fingerId, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for remove");
return;
@@ -617,7 +554,7 @@ public class FingerprintService extends SystemService {
// This effectively iterates through all sensors, but has to do so by finding all
// sensors under each provider.
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties();
for (FingerprintSensorPropertiesInternal prop : props) {
provider.scheduleRemoveAll(prop.sensorId, token, internalReceiver, userId,
@@ -652,7 +589,7 @@ public class FingerprintService extends SystemService {
try {
if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
provider.dumpProtoState(props.sensorId, proto, false);
@@ -660,14 +597,14 @@ public class FingerprintService extends SystemService {
}
proto.flush();
} else if (args.length > 0 && "--proto".equals(args[0])) {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
provider.dumpProtoMetrics(props.sensorId, fd);
}
}
} else {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
pw.println("Dumping for sensorId: " + props.sensorId
@@ -698,7 +635,7 @@ public class FingerprintService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetectedDeprecated, caller: "
+ opPackageName);
@@ -713,8 +650,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public boolean isHardwareDetected(int sensorId, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName);
return false;
@@ -730,7 +666,7 @@ public class FingerprintService extends SystemService {
return;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for rename");
return;
@@ -781,8 +717,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for hasEnrolledFingerprints, caller: " + opPackageName);
return false;
@@ -794,8 +729,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getLockoutModeForUser");
return LockoutTracker.LOCKOUT_NONE;
@@ -807,8 +741,7 @@ public class FingerprintService extends SystemService {
@Override
public void invalidateAuthenticatorId(int sensorId, int userId,
IInvalidationCallback callback) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
return;
@@ -819,8 +752,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getAuthenticatorId");
return 0;
@@ -832,8 +764,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void resetLockout(IBinder token, int sensorId, int userId,
@Nullable byte[] hardwareAuthToken, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
@@ -864,55 +795,38 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void registerAuthenticators(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
-
- // Some HAL might not be started before the system service and will cause the code below
- // to wait, and some of the operations below might take a significant amount of time to
- // complete (calls to the HALs). To avoid blocking the rest of system server we put
- // this on a background thread.
- final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
- true /* allowIo */);
- thread.start();
- final Handler handler = new Handler(thread.getLooper());
- handler.post(() -> {
+ mRegistry.registerAll(() -> {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ providers.addAll(getHidlProviders(hidlSensors));
List<String> aidlSensors = new ArrayList<>();
- final String[] instances =
- ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ final String[] instances = mAidlInstanceNameSupplier.get();
if (instances != null) {
aidlSensors.addAll(Lists.newArrayList(instances));
}
- registerAuthenticatorsForService(aidlSensors, hidlSensors);
+ providers.addAll(getAidlProviders(
+ Utils.filterAvailableHalInstances(getContext(), aidlSensors)));
+ return providers;
});
- thread.quitSafely();
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void addAuthenticatorsRegisteredCallback(
IFingerprintAuthenticatorsRegisteredCallback callback) {
- if (callback == null) {
- Slog.e(TAG, "addAuthenticatorsRegisteredCallback, callback is null");
- return;
- }
+ mRegistry.addAllRegisteredCallback(callback);
+ }
- final boolean registered;
- final boolean hasSensorProps;
- synchronized (mLock) {
- registered = mAuthenticatorsRegisteredCallbacks.register(callback);
- hasSensorProps = !mSensorProps.isEmpty();
- }
- if (registered && hasSensorProps) {
- broadcastAllAuthenticatorsRegistered();
- } else if (!registered) {
- Slog.e(TAG, "addAuthenticatorsRegisteredCallback failed to register callback");
- }
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override
+ public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
+ mBiometricStateCallback.registerBiometricStateListener(listener);
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onPointerDown(long requestId, int sensorId, int x, int y,
float minor, float major) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId);
return;
@@ -923,8 +837,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onPointerUp(long requestId, int sensorId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId);
return;
@@ -935,8 +848,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onUiReady(long requestId, int sensorId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId);
return;
@@ -947,8 +859,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
-
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
provider.setUdfpsOverlayController(controller);
}
}
@@ -956,22 +867,15 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
-
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
provider.setSidefpsController(controller);
}
}
- @Override
- public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
- Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- FingerprintService.this.registerBiometricStateListener(listener);
- }
-
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onPowerPressed() {
- Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
provider.onPowerPressed();
}
}
@@ -981,6 +885,7 @@ public class FingerprintService extends SystemService {
this(context, BiometricContext.getInstance(context),
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
(fqName) -> IFingerprint.Stub.asInterface(
Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName))));
}
@@ -988,61 +893,34 @@ public class FingerprintService extends SystemService {
@VisibleForTesting
FingerprintService(Context context,
BiometricContext biometricContext,
- Supplier<IBiometricService> biometricServiceProvider,
+ Supplier<IBiometricService> biometricServiceSupplier,
+ Supplier<String[]> aidlInstanceNameSupplier,
Function<String, IFingerprint> fingerprintProvider) {
super(context);
mBiometricContext = biometricContext;
- mBiometricServiceSupplier = biometricServiceProvider;
+ mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
mIFingerprintProvider = fingerprintProvider;
mAppOps = context.getSystemService(AppOpsManager.class);
mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
- mServiceProviders = new ArrayList<>();
- mBiometricStateCallback = new BiometricStateCallback();
- mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>();
- mSensorProps = new ArrayList<>();
+ mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
mHandler = new Handler(Looper.getMainLooper());
+ mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
+ mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ mBiometricStateCallback.start(mRegistry.getProviders());
+ }
+ });
}
- @VisibleForTesting
- void registerAuthenticatorsForService(@NonNull List<String> aidlInstanceNames,
+ @NonNull
+ private List<ServiceProvider> getHidlProviders(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
- addHidlProviders(hidlSensors);
- addAidlProviders(Utils.filterAvailableHalInstances(getContext(), aidlInstanceNames));
-
- final IBiometricService biometricService = mBiometricServiceSupplier.get();
-
- // Register each sensor individually with BiometricService
- for (ServiceProvider provider : mServiceProviders) {
- final List<FingerprintSensorPropertiesInternal> props =
- provider.getSensorProperties();
- for (FingerprintSensorPropertiesInternal prop : props) {
- final int sensorId = prop.sensorId;
- @BiometricManager.Authenticators.Types final int strength =
- Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength);
- final FingerprintAuthenticator authenticator = new FingerprintAuthenticator(
- mServiceWrapper, sensorId);
- try {
- biometricService.registerAuthenticator(sensorId, TYPE_FINGERPRINT,
- strength, authenticator);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId);
- }
- }
- }
-
- synchronized (mLock) {
- for (ServiceProvider provider : mServiceProviders) {
- mSensorProps.addAll(provider.getSensorProperties());
- }
- }
+ final List<ServiceProvider> providers = new ArrayList<>();
- broadcastCurrentEnrollmentState(null); // broadcasts to all listeners
- broadcastAllAuthenticatorsRegistered();
- }
-
- private void addHidlProviders(List<FingerprintSensorPropertiesInternal> hidlSensors) {
for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) {
final Fingerprint21 fingerprint21;
if ((Build.IS_USERDEBUG || Build.IS_ENG)
@@ -1059,11 +937,16 @@ public class FingerprintService extends SystemService {
mBiometricStateCallback, hidlSensor, mHandler,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
- mServiceProviders.add(fingerprint21);
+ providers.add(fingerprint21);
}
+
+ return providers;
}
- private void addAidlProviders(List<String> instances) {
+ @NonNull
+ private List<ServiceProvider> getAidlProviders(@NonNull List<String> instances) {
+ final List<ServiceProvider> providers = new ArrayList<>();
+
for (String instance : instances) {
final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
final IFingerprint fp = mIFingerprintProvider.apply(fqName);
@@ -1075,7 +958,7 @@ public class FingerprintService extends SystemService {
mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
mBiometricContext);
Slog.i(TAG, "Adding AIDL provider: " + fqName);
- mServiceProviders.add(provider);
+ providers.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
@@ -1083,38 +966,8 @@ public class FingerprintService extends SystemService {
Slog.e(TAG, "Unable to get declared service: " + fqName);
}
}
- }
- // Notifies the callbacks that all of the authenticators have been registered and removes the
- // invoked callbacks from the callback list.
- private void broadcastAllAuthenticatorsRegistered() {
- // Make a local copy of the data so it can be used outside of the synchronized block when
- // making Binder calls.
- final List<IFingerprintAuthenticatorsRegisteredCallback> callbacks = new ArrayList<>();
- final List<FingerprintSensorPropertiesInternal> props;
- synchronized (mLock) {
- if (!mSensorProps.isEmpty()) {
- props = new ArrayList<>(mSensorProps);
- } else {
- Slog.e(TAG, "mSensorProps is empty");
- return;
- }
- final int n = mAuthenticatorsRegisteredCallbacks.beginBroadcast();
- for (int i = 0; i < n; ++i) {
- final IFingerprintAuthenticatorsRegisteredCallback cb =
- mAuthenticatorsRegisteredCallbacks.getBroadcastItem(i);
- callbacks.add(cb);
- mAuthenticatorsRegisteredCallbacks.unregister(cb);
- }
- mAuthenticatorsRegisteredCallbacks.finishBroadcast();
- }
- for (IFingerprintAuthenticatorsRegisteredCallback cb : callbacks) {
- try {
- cb.onAllAuthenticatorsRegistered(props);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception in onAllAuthenticatorsRegistered", e);
- }
- }
+ return providers;
}
@Override
@@ -1122,51 +975,9 @@ public class FingerprintService extends SystemService {
publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper);
}
- @Nullable
- private ServiceProvider getProviderForSensor(int sensorId) {
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return provider;
- }
- }
- return null;
- }
-
- /**
- * For devices with only a single provider, returns that provider. If multiple providers,
- * returns the first one. If no providers, returns null.
- */
- @Nullable
- private Pair<Integer, ServiceProvider> getSingleProvider() {
- final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties();
- if (properties.isEmpty()) {
- Slog.e(TAG, "No providers found");
- return null;
- }
-
- // Theoretically we can just return the first provider, but maybe this is easier to
- // understand.
- final int sensorId = properties.get(0).sensorId;
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return new Pair<>(sensorId, provider);
- }
- }
-
- Slog.e(TAG, "Provider not found");
- return null;
- }
-
- @NonNull
- private List<FingerprintSensorPropertiesInternal> getSensorProperties() {
- synchronized (mLock) {
- return mSensorProps;
- }
- }
-
@NonNull
private List<Fingerprint> getEnrolledFingerprintsDeprecated(int userId, String opPackageName) {
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for getEnrolledFingerprintsDeprecated, caller: "
+ opPackageName);
@@ -1229,7 +1040,7 @@ public class FingerprintService extends SystemService {
if (Utils.isVirtualEnabled(getContext())) {
Slog.i(TAG, "Sync virtual enrollments");
final int userId = ActivityManager.getCurrentUser();
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */,
true /* favorHalEnrollments */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
new file mode 100644
index 000000000000..33810b764f23
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BiometricServiceRegistry;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Registry for {@link IFingerprintService} providers. */
+public class FingerprintServiceRegistry extends BiometricServiceRegistry<ServiceProvider,
+ FingerprintSensorPropertiesInternal, IFingerprintAuthenticatorsRegisteredCallback> {
+
+ private static final String TAG = "FingerprintServiceRegistry";
+
+ @NonNull
+ private final IFingerprintService mService;
+
+ /** Creates a new registry tied to the given service. */
+ public FingerprintServiceRegistry(@NonNull IFingerprintService service,
+ @Nullable Supplier<IBiometricService> biometricSupplier) {
+ super(biometricSupplier);
+ mService = service;
+ }
+
+ @Override
+ protected void registerService(@NonNull IBiometricService service,
+ @NonNull FingerprintSensorPropertiesInternal props) {
+ @BiometricManager.Authenticators.Types final int strength =
+ Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
+ try {
+ service.registerAuthenticator(props.sensorId, TYPE_FINGERPRINT, strength,
+ new FingerprintAuthenticator(mService, props.sensorId));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId);
+ }
+ }
+
+ @Override
+ protected void invokeRegisteredCallback(
+ @NonNull IFingerprintAuthenticatorsRegisteredCallback callback,
+ @NonNull List<FingerprintSensorPropertiesInternal> allProps) throws RemoteException {
+ callback.onAllAuthenticatorsRegistered(allProps);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 275d7e445a75..9075e7ec2080 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -28,14 +28,11 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
+import com.android.server.biometrics.sensors.BiometricServiceProvider;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.List;
/**
@@ -59,23 +56,8 @@ import java.util.List;
* fail safely.
*/
@SuppressWarnings("deprecation")
-public interface ServiceProvider {
- /**
- * Checks if the specified sensor is owned by this provider.
- */
- boolean containsSensor(int sensorId);
-
- @NonNull
- List<FingerprintSensorPropertiesInternal> getSensorProperties();
-
- /**
- * Returns the internal properties of the specified sensor, if owned by this provider.
- *
- * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor.
- * @return An object representing the internal properties of the specified sensor.
- */
- @Nullable
- FingerprintSensorPropertiesInternal getSensorProperties(int sensorId);
+public interface ServiceProvider extends
+ BiometricServiceProvider<FingerprintSensorPropertiesInternal> {
void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
@@ -126,16 +108,11 @@ public interface ServiceProvider {
void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments);
- boolean isHardwareDetected(int sensorId);
-
void rename(int sensorId, int fingerId, int userId, @NonNull String name);
@NonNull
List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId);
- @LockoutTracker.LockoutMode
- int getLockoutModeForUser(int sensorId, int userId);
-
/**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
@@ -143,7 +120,6 @@ public interface ServiceProvider {
void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback);
- long getAuthenticatorId(int sensorId, int userId);
void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major);
@@ -161,13 +137,6 @@ public interface ServiceProvider {
*/
void setSidefpsController(@NonNull ISidefpsController controller);
- void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
- boolean clearSchedulerBuffer);
-
- void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
-
- void dumpInternal(int sensorId, @NonNull PrintWriter pw);
-
@NonNull
ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 2dc005206b42..3fe6332fcaa0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -565,6 +565,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFingerprints(sensorId, userId).isEmpty();
+ }
+
+ @Override
public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index ed482f013869..0e6df8e0df77 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -789,6 +789,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFingerprints(sensorId, userId).isEmpty();
+ }
+
+ @Override
@LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2cbdad751b59..247635017539 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -813,13 +813,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
loadFromDisplayDeviceConfig(token, info);
- if (DEBUG) {
- Trace.beginAsyncSection("DisplayPowerController#updatePowerState", 0);
- }
updatePowerState();
- if (DEBUG) {
- Trace.endAsyncSection("DisplayPowerController#updatePowerState", 0);
- }
}
});
}
@@ -1147,6 +1141,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
private void updatePowerState() {
+ if (DEBUG) {
+ Trace.beginSection("DisplayPowerController#updatePowerState");
+ }
+ updatePowerStateInternal();
+ if (DEBUG) {
+ Trace.endSection();
+ }
+ }
+
+ private void updatePowerStateInternal() {
// Update the power state request.
final boolean mustNotify;
final int previousPolicy;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ea54b3001163..e6c2e7cae23e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -544,6 +544,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
|| message.getOpcode() == Constants.MESSAGE_SET_STREAM_PATH
|| message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) {
removeAction(ActiveSourceAction.class);
+ removeAction(OneTouchPlayAction.class);
return;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fa8b5c1b8086..3ee35036e9fd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1051,13 +1051,13 @@ public class HdmiControlService extends SystemService {
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
- mAddressAllocated = true;
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call
// onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
+ mAddressAllocated = true;
notifyAddressAllocated(allocatedDevices, initiatedBy);
// Reinvoke the saved display status callback once the local
// device is ready.
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 729c521a4ce0..477b8da61e0f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -86,7 +86,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -1328,17 +1327,16 @@ public class PreferencesHelper implements RankingConfig {
return null;
}
NotificationChannelGroup group = r.groups.get(groupId).clone();
- ArrayList channels = new ArrayList();
+ group.setChannels(new ArrayList<>());
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (groupId.equals(nc.getGroup())) {
- channels.add(nc);
+ group.addChannel(nc);
}
}
}
- group.setChannels(channels);
return group;
}
}
@@ -1351,10 +1349,7 @@ public class PreferencesHelper implements RankingConfig {
if (r == null) {
return null;
}
- if (r.groups.get(groupId) != null) {
- return r.groups.get(groupId).clone();
- }
- return null;
+ return r.groups.get(groupId);
}
}
@@ -1362,48 +1357,44 @@ public class PreferencesHelper implements RankingConfig {
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Objects.requireNonNull(pkg);
- List<NotificationChannelGroup> groups = new ArrayList<>();
+ Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return ParceledListSlice.emptyList();
}
- Map<String, ArrayList<NotificationChannel>> groupedChannels = new HashMap();
+ NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
- ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
- nc.getGroup(), new ArrayList<>());
- channels.add(nc);
- groupedChannels.put(nc.getGroup(), channels);
+ NotificationChannelGroup ncg = groups.get(nc.getGroup());
+ if (ncg == null) {
+ ncg = r.groups.get(nc.getGroup()).clone();
+ ncg.setChannels(new ArrayList<>());
+ groups.put(nc.getGroup(), ncg);
+
+ }
+ ncg.addChannel(nc);
}
} else {
- ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
- null, new ArrayList<>());
- channels.add(nc);
- groupedChannels.put(null, channels);
+ nonGrouped.addChannel(nc);
}
}
}
- for (NotificationChannelGroup group : r.groups.values()) {
- ArrayList<NotificationChannel> channels =
- groupedChannels.getOrDefault(group.getId(), new ArrayList<>());
- if (includeEmpty || !channels.isEmpty()) {
- NotificationChannelGroup clone = group.clone();
- clone.setChannels(channels);
- groups.add(clone);
- }
+ if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
+ groups.put(null, nonGrouped);
}
-
- if (includeNonGrouped && groupedChannels.containsKey(null)) {
- NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
- nonGrouped.setChannels(groupedChannels.get(null));
- groups.add(nonGrouped);
+ if (includeEmpty) {
+ for (NotificationChannelGroup group : r.groups.values()) {
+ if (!groups.containsKey(group.getId())) {
+ groups.put(group.getId(), group);
+ }
+ }
}
- return new ParceledListSlice<>(groups);
+ return new ParceledListSlice<>(new ArrayList<>(groups.values()));
}
}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index bdc571103ffd..5e0a18039152 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -131,6 +131,17 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
}
+ // For tests: just do the setting of various local variables without actually doing work
+ @VisibleForTesting
+ protected void initForTests(Context context, NotificationUsageStats usageStats,
+ LruCache peopleCache) {
+ mUserToContextMap = new ArrayMap<>();
+ mBaseContext = context;
+ mUsageStats = usageStats;
+ mPeopleCache = peopleCache;
+ mEnabled = true;
+ }
+
public RankingReconsideration process(NotificationRecord record) {
if (!mEnabled) {
if (VERBOSE) Slog.i(TAG, "disabled");
@@ -179,7 +190,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return NONE;
}
final PeopleRankingReconsideration prr =
- validatePeople(context, key, extras, null, affinityOut);
+ validatePeople(context, key, extras, null, affinityOut, null);
float affinity = affinityOut[0];
if (prr != null) {
@@ -224,15 +235,21 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return context;
}
- private RankingReconsideration validatePeople(Context context,
+ @VisibleForTesting
+ protected RankingReconsideration validatePeople(Context context,
final NotificationRecord record) {
final String key = record.getKey();
final Bundle extras = record.getNotification().extras;
final float[] affinityOut = new float[1];
+ ArraySet<String> phoneNumbersOut = new ArraySet<>();
final PeopleRankingReconsideration rr =
- validatePeople(context, key, extras, record.getPeopleOverride(), affinityOut);
+ validatePeople(context, key, extras, record.getPeopleOverride(), affinityOut,
+ phoneNumbersOut);
final float affinity = affinityOut[0];
record.setContactAffinity(affinity);
+ if (phoneNumbersOut.size() > 0) {
+ record.mergePhoneNumbers(phoneNumbersOut);
+ }
if (rr == null) {
mUsageStats.registerPeopleAffinity(record, affinity > NONE, affinity == STARRED_CONTACT,
true /* cached */);
@@ -243,7 +260,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
- List<String> peopleOverride, float[] affinityOut) {
+ List<String> peopleOverride, float[] affinityOut, ArraySet<String> phoneNumbersOut) {
float affinity = NONE;
if (extras == null) {
return null;
@@ -270,6 +287,15 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
if (lookupResult != null) {
affinity = Math.max(affinity, lookupResult.getAffinity());
+
+ // add all phone numbers associated with this lookup result, if they exist
+ // and if requested
+ if (phoneNumbersOut != null) {
+ ArraySet<String> phoneNumbers = lookupResult.getPhoneNumbers();
+ if (phoneNumbers != null && phoneNumbers.size() > 0) {
+ phoneNumbersOut.addAll(phoneNumbers);
+ }
+ }
}
}
if (++personIdx == MAX_PEOPLE) {
@@ -289,7 +315,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return new PeopleRankingReconsideration(context, key, pendingLookups);
}
- private String getCacheKey(int userId, String handle) {
+ @VisibleForTesting
+ protected static String getCacheKey(int userId, String handle) {
return Integer.toString(userId) + ":" + handle;
}
@@ -485,7 +512,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
}
- private static class LookupResult {
+ @VisibleForTesting
+ protected static class LookupResult {
private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
private final long mExpireMillis;
@@ -574,7 +602,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return mPhoneNumbers;
}
- private boolean isExpired() {
+ @VisibleForTesting
+ protected boolean isExpired() {
return mExpireMillis < System.currentTimeMillis();
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 51b36dd9c579..715967369ffb 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -39,7 +39,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -1417,18 +1416,16 @@ public final class OverlayManagerService extends SystemService {
private static void broadcastActionOverlayChanged(@NonNull final Set<String> targetPackages,
final int userId) {
- final PackageManagerInternal pmInternal =
- LocalServices.getService(PackageManagerInternal.class);
- final ActivityManagerInternal amInternal =
- LocalServices.getService(ActivityManagerInternal.class);
CollectionUtils.forEach(targetPackages, target -> {
final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
Uri.fromParts("package", target, null));
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- final int[] allowList = pmInternal.getVisibilityAllowList(target, userId);
- amInternal.broadcastIntent(intent, null /* resultTo */, null /* requiredPermissions */,
- false /* serialized */, userId, allowList, null /* filterExtrasForReceiver */,
- null /* bOptions */);
+ try {
+ ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null,
+ null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "broadcastActionOverlayChanged remote exception", e);
+ }
});
}
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index a878bfd46a54..ab998608f66d 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -113,6 +113,8 @@ public interface Computer extends PackageDataSnapshot {
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits);
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
+ long flags, int filterCallingUid, int userId);
+ @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
long flags, int userId);
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
long flags, int userId, int callingUid, boolean includeInstantApps);
@@ -500,6 +502,13 @@ public interface Computer extends PackageDataSnapshot {
boolean isComponentEffectivelyEnabled(@NonNull ComponentInfo componentInfo,
@UserIdInt int userId);
+ /**
+ * @return true if the runtime app user enabled state and the install-time app manifest enabled
+ * state are both effectively enabled for the given app. Or if the app cannot be found,
+ * returns false.
+ */
+ boolean isApplicationEffectivelyEnabled(@NonNull String packageName, @UserIdInt int userId);
+
@Nullable
KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 8ec3d2bc74ca..7c1777852382 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -608,6 +608,15 @@ public class ComputerEngine implements Computer {
resolveForStart, userId, intent);
}
+ @NonNull
+ @Override
+ public final List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
+ return queryIntentActivitiesInternal(
+ intent, resolvedType, flags, 0 /*privateResolveFlags*/, filterCallingUid,
+ userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
+ }
+
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
return queryIntentActivitiesInternal(
@@ -5412,6 +5421,26 @@ public class ComputerEngine implements Computer {
}
}
+ @Override
+ public boolean isApplicationEffectivelyEnabled(@NonNull String packageName,
+ @UserIdInt int userId) {
+ try {
+ int appEnabledSetting = mSettings.getApplicationEnabledSetting(packageName, userId);
+ if (appEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
+ final AndroidPackage pkg = getPackage(packageName);
+ if (pkg == null) {
+ // Should not happen because getApplicationEnabledSetting would have thrown
+ return false;
+ }
+ return pkg.isEnabled();
+ } else {
+ return appEnabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
+ }
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return false;
+ }
+ }
+
@Nullable
@Override
public KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index c8db2973f1eb..e969d93465f8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -309,7 +309,8 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
public final List<ResolveInfo> queryIntentActivities(
Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
int filterCallingUid, int userId) {
- return snapshot().queryIntentActivitiesInternal(intent, resolvedType, flags, userId);
+ return snapshot().queryIntentActivitiesInternal(intent, resolvedType, flags,
+ filterCallingUid, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 5731af68df95..297439f33a8b 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -315,4 +315,10 @@ public abstract class UserManagerInternal {
/** TODO(b/239982558): add javadoc / mention invalid_id is used to unassing */
public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
+
+ /**
+ * Returns {@code true} if the user is visible (as defined by
+ * {@link UserManager#isUserVisible()} in the given display.
+ */
+ public abstract boolean isUserVisible(@UserIdInt int userId, int displayId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 025e97318ba9..e423ea9170d7 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.UserManager.DISALLOW_USER_SWITCH;
import android.Manifest;
import android.accounts.Account;
@@ -1885,6 +1886,19 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isUserSwitcherEnabled(@UserIdInt int mUserId) {
+ boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.USER_SWITCHER_ENABLED,
+ Resources.getSystem().getBoolean(com.android.internal
+ .R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
+
+ return UserManager.supportsMultipleUsers()
+ && !hasUserRestriction(DISALLOW_USER_SWITCH, mUserId)
+ && !UserManager.isDeviceInDemoMode(mContext)
+ && multiUserSettingOn;
+ }
+
+ @Override
public boolean isRestricted(@UserIdInt int userId) {
if (userId != UserHandle.getCallingUserId()) {
checkCreateUsersPermission("query isRestricted for user " + userId);
@@ -2277,7 +2291,6 @@ public class UserManagerService extends IUserManager.Stub {
originatingUserId, local);
localChanged = updateLocalRestrictionsForTargetUsersLR(originatingUserId, local,
updatedLocalTargetUserIds);
-
if (isDeviceOwner) {
// Remember the global restriction owner userId to be able to make a distinction
// in getUserRestrictionSource on who set local policies.
@@ -4804,41 +4817,59 @@ public class UserManagerService extends IUserManager.Stub {
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(),
this::onPullAtom);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.MULTI_USER_INFO,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ this::onPullAtom);
}
/** Writes a UserInfo pulled atom for each user on the device. */
private int onPullAtom(int atomTag, List<StatsEvent> data) {
- if (atomTag != FrameworkStatsLog.USER_INFO) {
- Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
- return android.app.StatsManager.PULL_SKIP;
- }
- final List<UserInfo> users = getUsersInternal(true, true, true);
- final int size = users.size();
- if (size > 1) {
- for (int idx = 0; idx < size; idx++) {
- final UserInfo user = users.get(idx);
- final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
- final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
- .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
- ?
- user.userType : null;
-
- boolean isUserRunningUnlocked;
- synchronized (mUserStates) {
- isUserRunningUnlocked =
- mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+ if (atomTag == FrameworkStatsLog.USER_INFO) {
+ final List<UserInfo> users = getUsersInternal(true, true, true);
+ final int size = users.size();
+ if (size > 1) {
+ for (int idx = 0; idx < size; idx++) {
+ final UserInfo user = users.get(idx);
+ final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+ final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
+ ?
+ user.userType : null;
+
+ boolean isUserRunningUnlocked;
+ synchronized (mUserStates) {
+ isUserRunningUnlocked =
+ mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+ }
+
+ data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+ user.id,
+ userTypeStandard,
+ userTypeCustom,
+ user.flags,
+ user.creationTime,
+ user.lastLoggedInTime,
+ isUserRunningUnlocked
+ ));
+ }
+ }
+ } else if (atomTag == FrameworkStatsLog.MULTI_USER_INFO) {
+ if (UserManager.getMaxSupportedUsers() > 1) {
+ int deviceOwnerUserId = UserHandle.USER_NULL;
+
+ synchronized (mRestrictionsLock) {
+ deviceOwnerUserId = mDeviceOwnerUserId;
}
- data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
- user.id,
- userTypeStandard,
- userTypeCustom,
- user.flags,
- user.creationTime,
- user.lastLoggedInTime,
- isUserRunningUnlocked
- ));
+ data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.MULTI_USER_INFO,
+ UserManager.getMaxSupportedUsers(),
+ isUserSwitcherEnabled(deviceOwnerUserId)));
}
+ } else {
+ Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
+ return android.app.StatsManager.PULL_SKIP;
}
return android.app.StatsManager.PULL_SUCCESS;
}
@@ -6974,7 +7005,12 @@ public class UserManagerService extends IUserManager.Stub {
mUsersOnSecondaryDisplays.put(userId, displayId);
}
}
- }
+
+ @Override
+ public boolean isUserVisible(int userId, int displayId) {
+ return isUserVisibleOnDisplay(userId, displayId);
+ }
+ } // class LocalService
/**
* Check if user has restrictions
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 0a39e64ffcf2..47a3705388b6 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -25,6 +25,7 @@ import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
@@ -77,6 +78,7 @@ import com.android.server.sdksandbox.SdkSandboxManagerLocal;
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
final class VerifyingSession {
@@ -353,7 +355,7 @@ final class VerifyingSession {
}
final int verifierUserId = verifierUser.getIdentifier();
- String[] requiredVerifierPackages = mPm.mRequiredVerifierPackages;
+ List<String> requiredVerifierPackages = Arrays.asList(mPm.mRequiredVerifierPackages);
boolean requiredVerifierPackagesOverridden = false;
// Allow verifier override for ADB installations which could already be unverified using
@@ -377,8 +379,7 @@ final class VerifyingSession {
// are not adding a new way to disable verifications.
if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
requestedDisableVerification)) {
- requiredVerifierPackages = adbVerifierOverridePackages.toArray(
- new String[adbVerifierOverridePackages.size()]);
+ requiredVerifierPackages = adbVerifierOverridePackages;
requiredVerifierPackagesOverridden = true;
}
}
@@ -397,6 +398,16 @@ final class VerifyingSession {
*/
final Computer snapshot = mPm.snapshotComputer();
+ final int numRequiredVerifierPackages = requiredVerifierPackages.size();
+ for (int i = numRequiredVerifierPackages - 1; i >= 0; i--) {
+ if (!snapshot.isApplicationEffectivelyEnabled(requiredVerifierPackages.get(i),
+ SYSTEM_UID)) {
+ Slog.w(TAG,
+ "Required verifier: " + requiredVerifierPackages.get(i) + " is disabled");
+ requiredVerifierPackages.remove(i);
+ }
+ }
+
for (String requiredVerifierPackage : requiredVerifierPackages) {
final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
@@ -514,7 +525,7 @@ final class VerifyingSession {
}
}
- if (requiredVerifierPackages.length == 0) {
+ if (requiredVerifierPackages.size() == 0) {
Slog.e(TAG, "No required verifiers");
return;
}
@@ -532,7 +543,7 @@ final class VerifyingSession {
final Intent requiredIntent;
final String receiverPermission;
- if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.length == 1) {
+ if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.size() == 1) {
// Prod code OR test code+single verifier.
requiredIntent = new Intent(verification);
if (!requiredVerifierPackagesOverridden) {
@@ -657,7 +668,7 @@ final class VerifyingSession {
* @return true if verification should be performed
*/
private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
- String[] requiredVerifierPackages) {
+ List<String> requiredVerifierPackages) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 51bf55759962..937a7891a849 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2082,21 +2082,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+ boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
- return handleTransitionForKeyguardLw(false /* startKeyguardExitAnimation */,
- false /* notifyOccluded */);
+ // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
+ // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
+ // need to call IKeyguardService#keyguardGoingAway here.
+ return handleStartTransitionForKeyguardLw(keyguardGoingAway
+ && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation,
+ keyguardOccluding, duration);
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {
- // When app KEYGUARD_GOING_AWAY or (UN)OCCLUDE app transition is canceled, we need
- // to trigger relevant IKeyguardService calls to sync keyguard status in
- // WindowManagerService and SysUI.
- handleTransitionForKeyguardLw(
- keyguardGoingAwayCancelled /* startKeyguardExitAnimation */,
- keyguardOccludedCancelled /* notifyOccluded */);
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+ handleStartTransitionForKeyguardLw(
+ keyguardGoingAway, false /* keyguardOccludingStarted */,
+ 0 /* duration */);
}
});
@@ -3257,44 +3258,35 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void onKeyguardOccludedChangedLw(boolean occluded) {
- if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()
- && !WindowManagerService.sEnableShellTransitions) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
} else {
- setKeyguardOccludedLw(occluded, true /* notify */);
+ setKeyguardOccludedLw(occluded, false /* force */,
+ false /* transitionStarted */);
}
}
@Override
- public int applyKeyguardOcclusionChange(boolean notify) {
+ public int applyKeyguardOcclusionChange(boolean transitionStarted) {
if (mKeyguardOccludedChanged) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
+ mPendingKeyguardOccluded);
- if (setKeyguardOccludedLw(mPendingKeyguardOccluded, notify)) {
+ if (setKeyguardOccludedLw(mPendingKeyguardOccluded, false /* force */,
+ transitionStarted)) {
return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
}
}
return 0;
}
- /**
- * Called when keyguard related app transition starts, or cancelled.
- *
- * @param startKeyguardExitAnimation Trigger IKeyguardService#startKeyguardExitAnimation to
- * start keyguard exit animation.
- * @param notifyOccluded Trigger IKeyguardService#setOccluded binder call to notify whether
- * the top activity can occlude the keyguard or not.
- *
- * @return Whether the flags have changed and we have to redo the layout.
- */
- private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
- boolean notifyOccluded) {
- final int redoLayout = applyKeyguardOcclusionChange(notifyOccluded);
+ private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway,
+ boolean keyguardOccluding, long duration) {
+ final int redoLayout = applyKeyguardOcclusionChange(keyguardOccluding);
if (redoLayout != 0) return redoLayout;
- if (startKeyguardExitAnimation) {
+ if (keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
- startKeyguardExitAnimation(SystemClock.uptimeMillis());
+ startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
return 0;
}
@@ -3526,18 +3518,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* Updates the occluded state of the Keyguard.
*
* @param isOccluded Whether the Keyguard is occluded by another window.
- * @param notify Notify keyguard occlude status change immediately via
- * {@link com.android.internal.policy.IKeyguardService}.
+ * @param force notify the occluded status to KeyguardService and update flags even though
+ * occlude status doesn't change.
+ * @param transitionStarted {@code true} if keyguard (un)occluded transition started.
* @return Whether the flags have changed and we have to redo the layout.
*/
- private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
+ private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force,
+ boolean transitionStarted) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
- if (isKeyguardOccluded() == isOccluded) {
+ if (isKeyguardOccluded() == isOccluded && !force) {
return false;
}
- mKeyguardDelegate.setOccluded(isOccluded, notify);
- return mKeyguardDelegate.isShowing();
+
+ final boolean showing = mKeyguardDelegate.isShowing();
+ final boolean animate = showing && !isOccluded;
+ // When remote animation is enabled for keyguard (un)occlude transition, KeyguardService
+ // uses remote animation start as a signal to update its occlusion status ,so we don't need
+ // to notify here.
+ final boolean notify = !WindowManagerService.sEnableRemoteKeyguardOccludeAnimation
+ || !transitionStarted;
+ mKeyguardDelegate.setOccluded(isOccluded, animate, notify);
+ return showing;
}
/** {@inheritDoc} */
@@ -4933,10 +4935,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
- public void startKeyguardExitAnimation(long startTime) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
if (mKeyguardDelegate != null) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
- mKeyguardDelegate.startKeyguardExitAnimation(startTime);
+ mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 2b0405073323..4f00992c713e 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -171,10 +171,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
void onKeyguardOccludedChangedLw(boolean occluded);
/**
- * @param notify {@code true} if the status change should be immediately notified via
- * {@link com.android.internal.policy.IKeyguardService}
+ * Applies a keyguard occlusion change if one happened.
+ * @param transitionStarted Whether keyguard (un)occlude transition is starting or not.
*/
- int applyKeyguardOcclusionChange(boolean notify);
+ int applyKeyguardOcclusionChange(boolean transitionStarted);
/**
* Interface to the Window Manager state associated with a particular
@@ -1129,10 +1129,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Notifies the keyguard to start fading out.
- * @param startTime the start time of the animation in uptime milliseconds
*
+ * @param startTime the start time of the animation in uptime milliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- void startKeyguardExitAnimation(long startTime);
+ void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 7737421654ee..b79ac6f68be2 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -249,10 +249,10 @@ public class KeyguardServiceDelegate {
}
}
- public void setOccluded(boolean isOccluded, boolean notify) {
+ public void setOccluded(boolean isOccluded, boolean animate, boolean notify) {
if (mKeyguardService != null && notify) {
- if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
- mKeyguardService.setOccluded(isOccluded, false /* animate */);
+ if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
+ mKeyguardService.setOccluded(isOccluded, animate);
}
mKeyguardState.occluded = isOccluded;
}
@@ -394,9 +394,9 @@ public class KeyguardServiceDelegate {
}
}
- public void startKeyguardExitAnimation(long startTime) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
if (mKeyguardService != null) {
- mKeyguardService.startKeyguardExitAnimation(startTime, 0);
+ mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index df902c2916ba..fe4aa534df6f 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -657,7 +657,7 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
// Now that we have finally received all the data, we can tell mStats about it.
synchronized (mStats) {
- mStats.recordHistoryEventLocked(
+ mStats.addHistoryEventLocked(
elapsedRealtime,
uptime,
BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
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 37643c3c6f6f..202beb377b2c 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -108,7 +108,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsHistory;
-import com.android.internal.os.BatteryStatsHistory.HistoryStepDetailsCalculator;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderTransactionNameResolver;
@@ -174,6 +173,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
private static final boolean DEBUG_BINDER_STATS = false;
private static final boolean DEBUG_MEMORY = false;
+ private static final boolean DEBUG_HISTORY = false;
// TODO: remove "tcp" from network methods, since we measure total stats.
@@ -322,11 +322,6 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected Queue<UidToRemove> mPendingRemovedUids = new LinkedList<>();
- @NonNull
- BatteryStatsHistory copyHistory() {
- return mHistory.copy();
- }
-
@VisibleForTesting
public final class UidToRemove {
private final int mStartUid;
@@ -418,7 +413,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (changed) {
final long uptimeMs = mClock.uptimeMillis();
final long elapsedRealtimeMs = mClock.elapsedRealtime();
- mHistory.writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
}
@@ -673,16 +668,16 @@ public class BatteryStatsImpl extends BatteryStats {
/**
* Mapping isolated uids to the actual owning app uid.
*/
- private final SparseIntArray mIsolatedUids = new SparseIntArray();
+ final SparseIntArray mIsolatedUids = new SparseIntArray();
/**
* Internal reference count of isolated uids.
*/
- private final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
+ final SparseIntArray mIsolatedUidRefCounts = new SparseIntArray();
/**
* The statistics we have collected organized by uids.
*/
- private final SparseArray<BatteryStatsImpl.Uid> mUidStats = new SparseArray<>();
+ final SparseArray<BatteryStatsImpl.Uid> mUidStats = new SparseArray<>();
// A set of pools of currently active timers. When a timer is queried, we will divide the
// elapsed time by the number of active timers to arrive at that timer's share of the time.
@@ -690,21 +685,20 @@ public class BatteryStatsImpl extends BatteryStats {
// changes.
@VisibleForTesting
protected ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
- private final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
- private final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
- private final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
- new SparseArray<>();
- private final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>();
- private final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
+ final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
+ final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
+ final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
+ final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>();
+ final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();
// Last partial timers we use for distributing CPU usage.
@VisibleForTesting
@@ -719,24 +713,69 @@ public class BatteryStatsImpl extends BatteryStats {
protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(true);
private boolean mSystemReady;
- private boolean mShuttingDown;
-
- private final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
- private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator =
- new HistoryStepDetailsCalculatorImpl();
-
- private boolean mHaveBatteryLevel = false;
- private boolean mBatteryPluggedIn;
- private int mBatteryStatus;
- private int mBatteryLevel;
- private int mBatteryPlugType;
- private int mBatteryChargeUah;
- private int mBatteryHealth;
- private int mBatteryTemperature;
- private int mBatteryVoltageMv = -1;
+ boolean mShuttingDown;
+
+ final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
+
+ long mHistoryBaseTimeMs;
+ protected boolean mHaveBatteryLevel = false;
+ protected boolean mRecordingHistory = false;
+ int mNumHistoryItems;
+
+ private static final int HISTORY_TAG_INDEX_LIMIT = 0x7ffe;
+ private static final int MAX_HISTORY_TAG_STRING_LENGTH = 1024;
+
+ final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
+ private SparseArray<HistoryTag> mHistoryTags;
+ final Parcel mHistoryBuffer = Parcel.obtain();
+ final HistoryItem mHistoryLastWritten = new HistoryItem();
+ final HistoryItem mHistoryLastLastWritten = new HistoryItem();
+ final HistoryItem mHistoryAddTmp = new HistoryItem();
+ int mNextHistoryTagIdx = 0;
+ int mNumHistoryTagChars = 0;
+ int mHistoryBufferLastPos = -1;
+ int mActiveHistoryStates = 0xffffffff;
+ int mActiveHistoryStates2 = 0xffffffff;
+ long mLastHistoryElapsedRealtimeMs = 0;
+ long mTrackRunningHistoryElapsedRealtimeMs = 0;
+ long mTrackRunningHistoryUptimeMs = 0;
@NonNull
- private final BatteryStatsHistory mHistory;
+ final BatteryStatsHistory mBatteryStatsHistory;
+
+ final HistoryItem mHistoryCur = new HistoryItem();
+
+ // Used by computeHistoryStepDetails
+ HistoryStepDetails mLastHistoryStepDetails = null;
+ byte mLastHistoryStepLevel = 0;
+ final HistoryStepDetails mCurHistoryStepDetails = new HistoryStepDetails();
+ final HistoryStepDetails mTmpHistoryStepDetails = new HistoryStepDetails();
+
+ /**
+ * Total time (in milliseconds) spent executing in user code.
+ */
+ long mLastStepCpuUserTimeMs;
+ long mCurStepCpuUserTimeMs;
+ /**
+ * Total time (in milliseconds) spent executing in kernel code.
+ */
+ long mLastStepCpuSystemTimeMs;
+ long mCurStepCpuSystemTimeMs;
+ /**
+ * Times from /proc/stat (but measured in milliseconds).
+ */
+ long mLastStepStatUserTimeMs;
+ long mLastStepStatSystemTimeMs;
+ long mLastStepStatIOWaitTimeMs;
+ long mLastStepStatIrqTimeMs;
+ long mLastStepStatSoftIrqTimeMs;
+ long mLastStepStatIdleTimeMs;
+ long mCurStepStatUserTimeMs;
+ long mCurStepStatSystemTimeMs;
+ long mCurStepStatIOWaitTimeMs;
+ long mCurStepStatIrqTimeMs;
+ long mCurStepStatSoftIrqTimeMs;
+ long mCurStepStatIdleTimeMs;
private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator;
@@ -1352,6 +1391,7 @@ public class BatteryStatsImpl extends BatteryStats {
int mDischargeUnplugLevel;
int mDischargePlugLevel;
int mDischargeCurrentLevel;
+ int mCurrentBatteryLevel;
int mLowDischargeAmountSinceCharge;
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
@@ -1403,6 +1443,7 @@ public class BatteryStatsImpl extends BatteryStats {
private int mNumConnectivityChange;
+ private int mBatteryVoltageMv = -1;
private int mEstimatedBatteryCapacityMah = -1;
private int mLastLearnedBatteryCapacityUah = -1;
@@ -1586,27 +1627,28 @@ public class BatteryStatsImpl extends BatteryStats {
}
public BatteryStatsImpl(Clock clock) {
- this(clock, null);
+ this(clock, (File) null);
}
public BatteryStatsImpl(Clock clock, File historyDirectory) {
init(clock);
- mHandler = null;
- mConstants = new Constants(mHandler);
mStartClockTimeMs = clock.currentTimeMillis();
mCheckinFile = null;
mDailyFile = null;
if (historyDirectory == null) {
mStatsFile = null;
- mHistory = new BatteryStatsHistory(mStepDetailsCalculator, mClock);
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
} else {
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
- mHistory = new BatteryStatsHistory(historyDirectory, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer, historyDirectory,
+ this::getMaxHistoryFiles);
}
+ mHandler = null;
mPlatformIdleStateCallback = null;
mMeasuredEnergyRetriever = null;
mUserInfoProvider = null;
+ mConstants = new Constants(mHandler);
+ clearHistoryLocked();
}
private void init(Clock clock) {
@@ -3869,188 +3911,406 @@ public class BatteryStatsImpl extends BatteryStats {
return kmt;
}
- private class HistoryStepDetailsCalculatorImpl implements HistoryStepDetailsCalculator {
- private final HistoryStepDetails mDetails = new HistoryStepDetails();
-
- private boolean mHasHistoryStepDetails;
+ /**
+ * Returns the index for the specified tag. If this is the first time the tag is encountered
+ * while writing the current history buffer, the method returns
+ * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code>
+ */
+ private int writeHistoryTag(HistoryTag tag) {
+ if (tag.string == null) {
+ Slog.wtfStack(TAG, "writeHistoryTag called with null name");
+ }
+
+ final int stringLength = tag.string.length();
+ if (stringLength > MAX_HISTORY_TAG_STRING_LENGTH) {
+ Slog.e(TAG, "Long battery history tag: " + tag.string);
+ tag.string = tag.string.substring(0, MAX_HISTORY_TAG_STRING_LENGTH);
+ }
+
+ Integer idxObj = mHistoryTagPool.get(tag);
+ int idx;
+ if (idxObj != null) {
+ idx = idxObj;
+ if ((idx & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ mHistoryTagPool.put(tag, idx & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
+ }
+ return idx;
+ } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) {
+ idx = mNextHistoryTagIdx;
+ HistoryTag key = new HistoryTag();
+ key.setTo(tag);
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(key, idx);
+ mNextHistoryTagIdx++;
+
+ mNumHistoryTagChars += stringLength + 1;
+ if (mHistoryTags != null) {
+ mHistoryTags.put(idx, key);
+ }
+ return idx | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
+ } else {
+ // Tag pool overflow: include the tag itself in the parcel
+ return HISTORY_TAG_INDEX_LIMIT | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG;
+ }
+ }
- private int mLastHistoryStepLevel;
+ /*
+ The history delta format uses flags to denote further data in subsequent ints in the parcel.
+
+ There is always the first token, which may contain the delta time, or an indicator of
+ the length of the time (int or long) following this token.
+
+ First token: always present,
+ 31 23 15 7 0
+ █M|L|K|J|I|H|G|F█E|D|C|B|A|T|T|T█T|T|T|T|T|T|T|T█T|T|T|T|T|T|T|T█
+
+ T: the delta time if it is <= 0x7fffd. Otherwise 0x7fffe indicates an int immediately
+ follows containing the time, and 0x7ffff indicates a long immediately follows with the
+ delta time.
+ A: battery level changed and an int follows with battery data.
+ B: state changed and an int follows with state change data.
+ C: state2 has changed and an int follows with state2 change data.
+ D: wakelock/wakereason has changed and an wakelock/wakereason struct follows.
+ E: event data has changed and an event struct follows.
+ F: battery charge in coulombs has changed and an int with the charge follows.
+ G: state flag denoting that the mobile radio was active.
+ H: state flag denoting that the wifi radio was active.
+ I: state flag denoting that a wifi scan occurred.
+ J: state flag denoting that a wifi full lock was held.
+ K: state flag denoting that the gps was on.
+ L: state flag denoting that a wakelock was held.
+ M: state flag denoting that the cpu was running.
+
+ Time int/long: if T in the first token is 0x7ffff or 0x7fffe, then an int or long follows
+ with the time delta.
+
+ Battery level int: if A in the first token is set,
+ 31 23 15 7 0
+ █L|L|L|L|L|L|L|T█T|T|T|T|T|T|T|T█T|V|V|V|V|V|V|V█V|V|V|V|V|V|V|D█
+
+ D: indicates that extra history details follow.
+ V: the battery voltage.
+ T: the battery temperature.
+ L: the battery level (out of 100).
+
+ State change int: if B in the first token is set,
+ 31 23 15 7 0
+ █S|S|S|H|H|H|P|P█F|E|D|C|B| | |A█ | | | | | | | █ | | | | | | | █
+
+ A: wifi multicast was on.
+ B: battery was plugged in.
+ C: screen was on.
+ D: phone was scanning for signal.
+ E: audio was on.
+ F: a sensor was active.
+
+ State2 change int: if C in the first token is set,
+ 31 23 15 7 0
+ █M|L|K|J|I|H|H|G█F|E|D|C| | | | █ | | | | | | | █ |B|B|B|A|A|A|A█
+
+ A: 4 bits indicating the wifi supplicant state: {@link BatteryStats#WIFI_SUPPL_STATE_NAMES}.
+ B: 3 bits indicating the wifi signal strength: 0, 1, 2, 3, 4.
+ C: a bluetooth scan was active.
+ D: the camera was active.
+ E: bluetooth was on.
+ F: a phone call was active.
+ G: the device was charging.
+ H: 2 bits indicating the device-idle (doze) state: off, light, full
+ I: the flashlight was on.
+ J: wifi was on.
+ K: wifi was running.
+ L: video was playing.
+ M: power save mode was on.
+
+ Wakelock/wakereason struct: if D in the first token is set,
+ TODO(adamlesinski): describe wakelock/wakereason struct.
+
+ Event struct: if E in the first token is set,
+ TODO(adamlesinski): describe the event struct.
+
+ History step details struct: if D in the battery level int is set,
+ TODO(adamlesinski): describe the history step details struct.
+
+ Battery charge int: if F in the first token is set, an int representing the battery charge
+ in coulombs follows.
+ */
- /**
- * Total time (in milliseconds) spent executing in user code.
- */
- private long mLastStepCpuUserTimeMs;
- private long mCurStepCpuUserTimeMs;
- /**
- * Total time (in milliseconds) spent executing in kernel code.
- */
- private long mLastStepCpuSystemTimeMs;
- private long mCurStepCpuSystemTimeMs;
- /**
- * Times from /proc/stat (but measured in milliseconds).
- */
- private long mLastStepStatUserTimeMs;
- private long mLastStepStatSystemTimeMs;
- private long mLastStepStatIOWaitTimeMs;
- private long mLastStepStatIrqTimeMs;
- private long mLastStepStatSoftIrqTimeMs;
- private long mLastStepStatIdleTimeMs;
- private long mCurStepStatUserTimeMs;
- private long mCurStepStatSystemTimeMs;
- private long mCurStepStatIOWaitTimeMs;
- private long mCurStepStatIrqTimeMs;
- private long mCurStepStatSoftIrqTimeMs;
- private long mCurStepStatIdleTimeMs;
-
- @Override
- public HistoryStepDetails getHistoryStepDetails() {
- if (mBatteryLevel >= mLastHistoryStepLevel && mHasHistoryStepDetails) {
- mLastHistoryStepLevel = mBatteryLevel;
- return null;
- }
+ @GuardedBy("this")
+ public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) {
+ if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) {
+ dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS);
+ cur.writeToParcel(dest, 0);
+ return;
+ }
- // Perform a CPU update right after we do this collection, so we have started
- // collecting good data for the next step.
- requestImmediateCpuUpdate();
+ final long deltaTime = cur.time - last.time;
+ final int lastBatteryLevelInt = buildBatteryLevelInt(last);
+ final int lastStateInt = buildStateInt(last);
- if (mPlatformIdleStateCallback != null) {
- mDetails.statSubsystemPowerState =
- mPlatformIdleStateCallback.getSubsystemLowPowerStats();
- if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
- mDetails.statSubsystemPowerState);
- }
-
- if (!mHasHistoryStepDetails) {
- // We are not generating a delta, so all we need to do is reset the stats
- // we will later be doing a delta from.
- final int uidCount = mUidStats.size();
- for (int i = 0; i < uidCount; i++) {
- final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
- uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
- uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
- }
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
- mDetails.clear();
+ int deltaTimeToken;
+ if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) {
+ deltaTimeToken = BatteryStatsHistory.DELTA_TIME_LONG;
+ } else if (deltaTime >= BatteryStatsHistory.DELTA_TIME_ABS) {
+ deltaTimeToken = BatteryStatsHistory.DELTA_TIME_INT;
+ } else {
+ deltaTimeToken = (int)deltaTime;
+ }
+ int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK);
+ final int includeStepDetails = mLastHistoryStepLevel > cur.batteryLevel
+ ? BatteryStatsHistory.BATTERY_DELTA_LEVEL_FLAG : 0;
+ final boolean computeStepDetails = includeStepDetails != 0
+ || mLastHistoryStepDetails == null;
+ final int batteryLevelInt = buildBatteryLevelInt(cur) | includeStepDetails;
+ final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
+ if (batteryLevelIntChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_BATTERY_LEVEL_FLAG;
+ }
+ final int stateInt = buildStateInt(cur);
+ final boolean stateIntChanged = stateInt != lastStateInt;
+ if (stateIntChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
+ }
+ final boolean state2IntChanged = cur.states2 != last.states2;
+ if (state2IntChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG;
+ }
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ firstToken |= BatteryStatsHistory.DELTA_WAKELOCK_FLAG;
+ }
+ if (cur.eventCode != HistoryItem.EVENT_NONE) {
+ firstToken |= BatteryStatsHistory.DELTA_EVENT_FLAG;
+ }
+
+ final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah;
+ if (batteryChargeChanged) {
+ firstToken |= BatteryStatsHistory.DELTA_BATTERY_CHARGE_FLAG;
+ }
+ dest.writeInt(firstToken);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
+ + " deltaTime=" + deltaTime);
+
+ if (deltaTimeToken >= BatteryStatsHistory.DELTA_TIME_INT) {
+ if (deltaTimeToken == BatteryStatsHistory.DELTA_TIME_INT) {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
+ dest.writeInt((int)deltaTime);
} else {
- if (DEBUG) {
- Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
- + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
- + " irq=" + mLastStepStatIrqTimeMs + " sirq="
- + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
- Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
- + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
- + " irq=" + mCurStepStatIrqTimeMs + " sirq="
- + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
- }
- mDetails.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
- mDetails.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
- mDetails.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
- mDetails.statSystemTime =
- (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
- mDetails.statIOWaitTime =
- (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
- mDetails.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
- mDetails.statSoftIrqTime =
- (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
- mDetails.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
- mDetails.appCpuUid1 = mDetails.appCpuUid2 = mDetails.appCpuUid3 = -1;
- mDetails.appCpuUTime1 = mDetails.appCpuUTime2 = mDetails.appCpuUTime3 = 0;
- mDetails.appCpuSTime1 = mDetails.appCpuSTime2 = mDetails.appCpuSTime3 = 0;
- final int uidCount = mUidStats.size();
- for (int i = 0; i < uidCount; i++) {
- final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
- final int totalUTimeMs =
- (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
- final int totalSTimeMs =
- (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
- final int totalTimeMs = totalUTimeMs + totalSTimeMs;
- uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
- uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
- if (totalTimeMs <= (mDetails.appCpuUTime3 + mDetails.appCpuSTime3)) {
- continue;
- }
- if (totalTimeMs <= (mDetails.appCpuUTime2 + mDetails.appCpuSTime2)) {
- mDetails.appCpuUid3 = uid.mUid;
- mDetails.appCpuUTime3 = totalUTimeMs;
- mDetails.appCpuSTime3 = totalSTimeMs;
- } else {
- mDetails.appCpuUid3 = mDetails.appCpuUid2;
- mDetails.appCpuUTime3 = mDetails.appCpuUTime2;
- mDetails.appCpuSTime3 = mDetails.appCpuSTime2;
- if (totalTimeMs <= (mDetails.appCpuUTime1 + mDetails.appCpuSTime1)) {
- mDetails.appCpuUid2 = uid.mUid;
- mDetails.appCpuUTime2 = totalUTimeMs;
- mDetails.appCpuSTime2 = totalSTimeMs;
- } else {
- mDetails.appCpuUid2 = mDetails.appCpuUid1;
- mDetails.appCpuUTime2 = mDetails.appCpuUTime1;
- mDetails.appCpuSTime2 = mDetails.appCpuSTime1;
- mDetails.appCpuUid1 = uid.mUid;
- mDetails.appCpuUTime1 = totalUTimeMs;
- mDetails.appCpuSTime1 = totalSTimeMs;
- }
- }
- }
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
+ dest.writeLong(deltaTime);
+ }
+ }
+ if (batteryLevelIntChanged) {
+ dest.writeInt(batteryLevelInt);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
+ + Integer.toHexString(batteryLevelInt)
+ + " batteryLevel=" + cur.batteryLevel
+ + " batteryTemp=" + cur.batteryTemperature
+ + " batteryVolt=" + (int)cur.batteryVoltage);
+ }
+ if (stateIntChanged) {
+ dest.writeInt(stateInt);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
+ + Integer.toHexString(stateInt)
+ + " batteryStatus=" + cur.batteryStatus
+ + " batteryHealth=" + cur.batteryHealth
+ + " batteryPlugType=" + cur.batteryPlugType
+ + " states=0x" + Integer.toHexString(cur.states));
+ }
+ if (state2IntChanged) {
+ dest.writeInt(cur.states2);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+ + Integer.toHexString(cur.states2));
+ }
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ int wakeLockIndex;
+ int wakeReasonIndex;
+ if (cur.wakelockTag != null) {
+ wakeLockIndex = writeHistoryTag(cur.wakelockTag);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ } else {
+ wakeLockIndex = 0xffff;
+ }
+ if (cur.wakeReasonTag != null) {
+ wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ } else {
+ wakeReasonIndex = 0xffff;
+ }
+ dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
+ if (cur.wakelockTag != null
+ && (wakeLockIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ cur.wakelockTag.writeToParcel(dest, 0);
+ cur.tagsFirstOccurrence = true;
+ }
+ if (cur.wakeReasonTag != null
+ && (wakeReasonIndex & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ cur.wakeReasonTag.writeToParcel(dest, 0);
+ cur.tagsFirstOccurrence = true;
}
-
- mHasHistoryStepDetails = mBatteryLevel <= mLastHistoryStepLevel;
- mLastHistoryStepLevel = mBatteryLevel;
-
- return mDetails;
}
-
- public void addCpuStats(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
- int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
- int statSoftIrqTimeMs, int statIdleTimeMs) {
- if (DEBUG) {
- Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
- + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
- + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
- + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
+ if (cur.eventCode != HistoryItem.EVENT_NONE) {
+ final int index = writeHistoryTag(cur.eventTag);
+ final int codeAndIndex = (cur.eventCode & 0xffff) | (index << 16);
+ dest.writeInt(codeAndIndex);
+ if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
+ cur.eventTag.writeToParcel(dest, 0);
+ cur.tagsFirstOccurrence = true;
}
- mCurStepCpuUserTimeMs += totalUTimeMs;
- mCurStepCpuSystemTimeMs += totalSTimeMs;
- mCurStepStatUserTimeMs += statUserTimeMs;
- mCurStepStatSystemTimeMs += statSystemTimeMs;
- mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
- mCurStepStatIrqTimeMs += statIrqTimeMs;
- mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
- mCurStepStatIdleTimeMs += statIdleTimeMs;
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: event=" + cur.eventCode + " tag=#"
+ + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":"
+ + cur.eventTag.string);
}
+ if (computeStepDetails) {
+ if (mPlatformIdleStateCallback != null) {
+ mCurHistoryStepDetails.statSubsystemPowerState =
+ mPlatformIdleStateCallback.getSubsystemLowPowerStats();
+ if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" +
+ mCurHistoryStepDetails.statSubsystemPowerState);
- @Override
- public void clear() {
- mHasHistoryStepDetails = false;
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
+ }
+ computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails);
+ if (includeStepDetails != 0) {
+ mCurHistoryStepDetails.writeToParcel(dest);
+ }
+ cur.stepDetails = mCurHistoryStepDetails;
+ mLastHistoryStepDetails = mCurHistoryStepDetails;
+ } else {
+ cur.stepDetails = null;
+ }
+ if (mLastHistoryStepLevel < cur.batteryLevel) {
+ mLastHistoryStepDetails = null;
+ }
+ mLastHistoryStepLevel = cur.batteryLevel;
+
+ if (batteryChargeChanged) {
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah);
+ dest.writeInt(cur.batteryChargeUah);
+ }
+ dest.writeDouble(cur.modemRailChargeMah);
+ dest.writeDouble(cur.wifiRailChargeMah);
+ }
+
+ private int buildBatteryLevelInt(HistoryItem h) {
+ return ((((int)h.batteryLevel)<<25)&0xfe000000)
+ | ((((int)h.batteryTemperature)<<15)&0x01ff8000)
+ | ((((int)h.batteryVoltage)<<1)&0x00007ffe);
+ }
+
+ private int buildStateInt(HistoryItem h) {
+ int plugType = 0;
+ if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_AC) != 0) {
+ plugType = 1;
+ } else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_USB) != 0) {
+ plugType = 2;
+ } else if ((h.batteryPlugType&BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0) {
+ plugType = 3;
+ }
+ return ((h.batteryStatus & BatteryStatsHistory.STATE_BATTERY_STATUS_MASK)
+ << BatteryStatsHistory.STATE_BATTERY_STATUS_SHIFT)
+ | ((h.batteryHealth & BatteryStatsHistory.STATE_BATTERY_HEALTH_MASK)
+ << BatteryStatsHistory.STATE_BATTERY_HEALTH_SHIFT)
+ | ((plugType & BatteryStatsHistory.STATE_BATTERY_PLUG_MASK)
+ << BatteryStatsHistory.STATE_BATTERY_PLUG_SHIFT)
+ | (h.states & (~BatteryStatsHistory.STATE_BATTERY_MASK));
+ }
+
+ private void computeHistoryStepDetails(final HistoryStepDetails out,
+ final HistoryStepDetails last) {
+ final HistoryStepDetails tmp = last != null ? mTmpHistoryStepDetails : out;
+
+ // Perform a CPU update right after we do this collection, so we have started
+ // collecting good data for the next step.
+ requestImmediateCpuUpdate();
+
+ if (last == null) {
+ // We are not generating a delta, so all we need to do is reset the stats
+ // we will later be doing a delta from.
+ final int NU = mUidStats.size();
+ for (int i=0; i<NU; i++) {
+ final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+ uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
+ }
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
+ tmp.clear();
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
+ + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
+ + " irq=" + mLastStepStatIrqTimeMs + " sirq="
+ + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
+ Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
+ + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
+ + " irq=" + mCurStepStatIrqTimeMs + " sirq="
+ + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
+ }
+ out.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
+ out.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
+ out.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
+ out.statSystemTime = (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
+ out.statIOWaitTime = (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
+ out.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
+ out.statSoftIrqTime = (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
+ out.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
+ out.appCpuUid1 = out.appCpuUid2 = out.appCpuUid3 = -1;
+ out.appCpuUTime1 = out.appCpuUTime2 = out.appCpuUTime3 = 0;
+ out.appCpuSTime1 = out.appCpuSTime2 = out.appCpuSTime3 = 0;
+ final int NU = mUidStats.size();
+ for (int i=0; i<NU; i++) {
+ final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ final int totalUTimeMs = (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
+ final int totalSTimeMs = (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
+ final int totalTimeMs = totalUTimeMs + totalSTimeMs;
+ uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+ uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
+ if (totalTimeMs <= (out.appCpuUTime3 + out.appCpuSTime3)) {
+ continue;
+ }
+ if (totalTimeMs <= (out.appCpuUTime2 + out.appCpuSTime2)) {
+ out.appCpuUid3 = uid.mUid;
+ out.appCpuUTime3 = totalUTimeMs;
+ out.appCpuSTime3 = totalSTimeMs;
+ } else {
+ out.appCpuUid3 = out.appCpuUid2;
+ out.appCpuUTime3 = out.appCpuUTime2;
+ out.appCpuSTime3 = out.appCpuSTime2;
+ if (totalTimeMs <= (out.appCpuUTime1 + out.appCpuSTime1)) {
+ out.appCpuUid2 = uid.mUid;
+ out.appCpuUTime2 = totalUTimeMs;
+ out.appCpuSTime2 = totalSTimeMs;
+ } else {
+ out.appCpuUid2 = out.appCpuUid1;
+ out.appCpuUTime2 = out.appCpuUTime1;
+ out.appCpuSTime2 = out.appCpuSTime1;
+ out.appCpuUid1 = uid.mUid;
+ out.appCpuUTime1 = totalUTimeMs;
+ out.appCpuSTime1 = totalSTimeMs;
+ }
+ }
}
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
}
@GuardedBy("this")
@Override
public void commitCurrentHistoryBatchLocked() {
- mHistory.commitCurrentHistoryBatchLocked();
+ mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
@GuardedBy("this")
@@ -4066,9 +4326,191 @@ public class BatteryStatsImpl extends BatteryStats {
}
@GuardedBy("this")
- public void recordHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+ void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ if (!mHaveBatteryLevel || !mRecordingHistory) {
+ return;
+ }
+
+ final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
+ final int diffStates = mHistoryLastWritten.states^(cur.states&mActiveHistoryStates);
+ final int diffStates2 = mHistoryLastWritten.states2^(cur.states2&mActiveHistoryStates2);
+ final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
+ final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
+ if (DEBUG) {
+ Slog.i(TAG, "ADD: tdelta=" + timeDiffMs + " diff="
+ + Integer.toHexString(diffStates) + " lastDiff="
+ + Integer.toHexString(lastDiffStates) + " diff2="
+ + Integer.toHexString(diffStates2) + " lastDiff2="
+ + Integer.toHexString(lastDiffStates2));
+ }
+ if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
+ && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
+ && (diffStates2&lastDiffStates2) == 0
+ && (!mHistoryLastWritten.tagsFirstOccurrence && !cur.tagsFirstOccurrence)
+ && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+ && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
+ && mHistoryLastWritten.stepDetails == null
+ && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
+ || cur.eventCode == HistoryItem.EVENT_NONE)
+ && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+ && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+ && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+ && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+ && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+ && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
+ // We can merge this new change in with the last one. Merging is
+ // allowed as long as only the states have changed, and within those states
+ // as long as no bit has changed both between now and the last entry, as
+ // well as the last entry and the one before it (so we capture any toggles).
+ if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
+ mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
+ mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
+ mHistoryBufferLastPos = -1;
+ elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTimeMs;
+ // If the last written history had a wakelock tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakelockTag != null) {
+ cur.wakelockTag = cur.localWakelockTag;
+ cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+ }
+ // If the last written history had a wake reason tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakeReasonTag != null) {
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+ }
+ // If the last written history had an event, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have an event.
+ if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
+ cur.eventCode = mHistoryLastWritten.eventCode;
+ cur.eventTag = cur.localEventTag;
+ cur.eventTag.setTo(mHistoryLastWritten.eventTag);
+ }
+ mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+ }
+ final int dataSize = mHistoryBuffer.dataSize();
+
+ if (dataSize >= mConstants.MAX_HISTORY_BUFFER) {
+ //open a new history file.
+ final long start = SystemClock.uptimeMillis();
+ writeHistoryLocked();
+ if (DEBUG) {
+ Slog.d(TAG, "addHistoryBufferLocked writeHistoryLocked takes ms:"
+ + (SystemClock.uptimeMillis() - start));
+ }
+ mBatteryStatsHistory.startNextFile();
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
+ mHistoryBufferLastPos = -1;
+ mHistoryLastWritten.clear();
+ mHistoryLastLastWritten.clear();
+
+ // Mark every entry in the pool with a flag indicating that the tag
+ // has not yet been encountered while writing the current history buffer.
+ for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
+ entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
+ }
+ // Make a copy of mHistoryCur.
+ HistoryItem copy = new HistoryItem();
+ copy.setTo(cur);
+ // startRecordingHistory will reset mHistoryCur.
+ startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
+ // Add the copy into history buffer.
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, copy);
+ return;
+ }
+
+ if (dataSize == 0) {
+ // The history is currently empty; we need it to start with a time stamp.
+ cur.currentTime = mClock.currentTimeMillis();
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
+ }
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
+ }
+
+ @GuardedBy("this")
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
+ if (mBatteryStatsHistoryIterator != null) {
+ throw new IllegalStateException("Can't do this while iterating history!");
+ }
+ mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
+ mHistoryLastLastWritten.setTo(mHistoryLastWritten);
+ final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
+ mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
+ mHistoryLastWritten.tagsFirstOccurrence = hasTags;
+ mHistoryLastWritten.states &= mActiveHistoryStates;
+ mHistoryLastWritten.states2 &= mActiveHistoryStates2;
+ writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
+ mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
+ cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
+ cur.eventCode = HistoryItem.EVENT_NONE;
+ cur.eventTag = null;
+ cur.tagsFirstOccurrence = false;
+ if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ + " now " + mHistoryBuffer.dataPosition()
+ + " size is now " + mHistoryBuffer.dataSize());
+ }
+
+ @GuardedBy("this")
+ void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
+ if (mTrackRunningHistoryElapsedRealtimeMs != 0) {
+ final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs;
+ final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs;
+ if (diffUptimeMs < (diffElapsedMs - 20)) {
+ final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs);
+ mHistoryAddTmp.setTo(mHistoryLastWritten);
+ mHistoryAddTmp.wakelockTag = null;
+ mHistoryAddTmp.wakeReasonTag = null;
+ mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+ mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+ addHistoryRecordInnerLocked(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
+ }
+ }
+ mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+ mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs;
+ mTrackRunningHistoryUptimeMs = uptimeMs;
+ addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ }
+
+ @GuardedBy("this")
+ void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
+ }
+
+ @GuardedBy("this")
+ public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
String name, int uid) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, code, name, uid);
+ mHistoryCur.eventCode = code;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.string = name;
+ mHistoryCur.eventTag.uid = uid;
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ }
+
+ @GuardedBy("this")
+ void clearHistoryLocked() {
+ if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
+ mHistoryBaseTimeMs = 0;
+ mLastHistoryElapsedRealtimeMs = 0;
+ mTrackRunningHistoryElapsedRealtimeMs = 0;
+ mTrackRunningHistoryUptimeMs = 0;
+
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
+ mHistoryLastLastWritten.clear();
+ mHistoryLastWritten.clear();
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
+ mHistoryBufferLastPos = -1;
+ mActiveHistoryStates = 0xffffffff;
+ mActiveHistoryStates2 = 0xffffffff;
}
@GuardedBy("this")
@@ -4221,13 +4663,13 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(code, name, uid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, code, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, code, name, uid);
}
@GuardedBy("this")
public void noteCurrentTimeChangedLocked(long currentTimeMs,
long elapsedRealtimeMs, long uptimeMs) {
- mHistory.recordCurrentTimeChange(elapsedRealtimeMs, uptimeMs, currentTimeMs);
+ recordCurrentTimeChangeLocked(currentTimeMs, elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
@@ -4244,7 +4686,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mRecordAllHistory) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_START, name, uid);
}
@GuardedBy("this")
@@ -4302,7 +4744,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mRecordAllHistory) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_FINISH, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PROC_FINISH,
+ name, uid);
}
@GuardedBy("this")
@@ -4318,7 +4761,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_START, name, uid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_START, name, uid);
}
@GuardedBy("this")
@@ -4334,7 +4777,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_SYNC_FINISH, name, uid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_FINISH, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SYNC_FINISH,
+ name, uid);
}
@GuardedBy("this")
@@ -4350,7 +4794,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_START, name, uid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_START, name, uid);
}
@GuardedBy("this")
@@ -4368,7 +4812,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mActiveEvents.updateState(HistoryItem.EVENT_JOB_FINISH, name, uid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_JOB_FINISH, name, uid);
}
@GuardedBy("this")
@@ -4416,7 +4860,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < workSource.size(); ++i) {
uid = mapUid(workSource.getUid(i));
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
@@ -4425,7 +4869,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < workChains.size(); ++i) {
uid = mapUid(workChains.get(i).getAttributionUid());
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
}
@@ -4433,7 +4877,7 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (mActiveEvents.updateState(historyItem, name, uid, 0)) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, historyItem, name, uid);
}
}
}
@@ -4508,7 +4952,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- mHistory.recordEvent(mSecRealtime, mSecUptime,
+ addHistoryEventLocked(mSecRealtime, mSecUptime,
HistoryItem.EVENT_PROC_FINISH, ent.getKey(), uids.keyAt(j));
}
}
@@ -4523,8 +4967,8 @@ public class BatteryStatsImpl extends BatteryStats {
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- mHistory.recordEvent(mSecRealtime, mSecUptime, HistoryItem.EVENT_PROC_START,
- ent.getKey(), uids.keyAt(j));
+ addHistoryEventLocked(mSecRealtime, mSecUptime,
+ HistoryItem.EVENT_PROC_START, ent.getKey(), uids.keyAt(j));
}
}
}
@@ -4567,19 +5011,30 @@ public class BatteryStatsImpl extends BatteryStats {
if (mRecordAllHistory) {
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
mappedUid, 0)) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_WAKE_LOCK_START, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = historyName;
+ mHistoryCur.wakelockTag.uid = mappedUid;
mWakeLockImportant = !unimportantForLogging;
- mHistory.recordWakelockStartEvent(elapsedRealtimeMs, uptimeMs, historyName,
- mappedUid);
- } else if (!mWakeLockImportant && !unimportantForLogging) {
- if (mHistory.maybeUpdateWakelockTag(elapsedRealtimeMs, uptimeMs, historyName,
- mappedUid)) {
- mWakeLockImportant = true;
- }
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ } else if (!mWakeLockImportant && !unimportantForLogging
+ && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE) {
+ if (mHistoryLastWritten.wakelockTag != null) {
+ // We'll try to update the last tag.
+ mHistoryLastWritten.wakelockTag = null;
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = historyName;
+ mHistoryCur.wakelockTag.uid = mappedUid;
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+ }
+ mWakeLockImportant = true;
}
mWakeLockNesting++;
}
@@ -4632,13 +5087,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
mappedUid, 0)) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, mappedUid);
}
}
if (mWakeLockNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WAKE_LOCK_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
if (mappedUid >= 0) {
@@ -4829,7 +5286,7 @@ public class BatteryStatsImpl extends BatteryStats {
mappedUid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_START,
historyName, mappedUid);
if (mappedUid != uid) {
// Prevent the isolated uid mapping from being removed while the wakelock is
@@ -4882,7 +5339,7 @@ public class BatteryStatsImpl extends BatteryStats {
mappedUid, 0)) {
return;
}
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_LONG_WAKE_LOCK_FINISH,
historyName, mappedUid);
if (mappedUid != uid) {
// Decrement the ref count for the isolated uid and delete the mapping if uneeded.
@@ -4904,10 +5361,15 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason \"" + reason +"\": "
+ + Integer.toHexString(mHistoryCur.states));
aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
- mHistory.recordWakeupEvent(elapsedRealtimeMs, uptimeMs, reason);
+ mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+ mHistoryCur.wakeReasonTag.string = reason;
+ mHistoryCur.wakeReasonTag.uid = 0;
mLastWakeupReason = reason;
mLastWakeupUptimeMs = uptimeMs;
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
@GuardedBy("this")
@@ -4918,11 +5380,22 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void finishAddingCpuLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
- int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
- int statSoftIrqTimeMs, int statIdleTimeMs) {
- mStepDetailsCalculator.addCpuStats(totalUTimeMs, totalSTimeMs, statUserTimeMs,
- statSystemTimeMs, statIOWaitTimeMs, statIrqTimeMs,
- statSoftIrqTimeMs, statIdleTimeMs);
+ int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
+ int statSoftIrqTimeMs, int statIdleTimeMs) {
+ if (DEBUG) {
+ Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
+ + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
+ + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
+ + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
+ }
+ mCurStepCpuUserTimeMs += totalUTimeMs;
+ mCurStepCpuSystemTimeMs += totalSTimeMs;
+ mCurStepStatUserTimeMs += statUserTimeMs;
+ mCurStepStatSystemTimeMs += statSystemTimeMs;
+ mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
+ mCurStepStatIrqTimeMs += statIrqTimeMs;
+ mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
+ mCurStepStatIdleTimeMs += statIdleTimeMs;
}
public void noteProcessDiedLocked(int uid, int pid) {
@@ -4952,8 +5425,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mSensorNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_SENSOR_ON_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mSensorNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -4970,8 +5445,10 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
mSensorNesting--;
if (mSensorNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_SENSOR_ON_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteStopSensor(sensor, elapsedRealtimeMs);
@@ -5021,8 +5498,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
final int mappedUid = mapUid(uid);
if (mGpsNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_GPS_ON_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mGpsNesting++;
@@ -5047,8 +5526,10 @@ public class BatteryStatsImpl extends BatteryStats {
final int mappedUid = mapUid(uid);
mGpsNesting--;
if (mGpsNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_GPS_ON_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
mGpsSignalQualityBin = -1;
}
@@ -5081,9 +5562,12 @@ public class BatteryStatsImpl extends BatteryStats {
if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtimeMs);
}
- mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs, signalLevel);
+ mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+ | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mGpsSignalQualityBin = signalLevel;
}
+ return;
}
@GuardedBy("this")
@@ -5256,33 +5740,41 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- int startStates = 0;
- int stopStates = 0;
+ boolean updateHistory = false;
if (Display.isDozeState(state) && !Display.isDozeState(oldState)) {
- startStates |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.startRunningLocked(elapsedRealtimeMs);
+ updateHistory = true;
} else if (Display.isDozeState(oldState) && !Display.isDozeState(state)) {
- stopStates |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.stopRunningLocked(elapsedRealtimeMs);
+ updateHistory = true;
}
if (Display.isOnState(state)) {
- startStates |= HistoryItem.STATE_SCREEN_ON_FLAG;
+ mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ + Integer.toHexString(mHistoryCur.states));
mScreenOnTimer.startRunningLocked(elapsedRealtimeMs);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.startRunningLocked(elapsedRealtimeMs);
}
+ updateHistory = true;
} else if (Display.isOnState(oldState)) {
- stopStates |= HistoryItem.STATE_SCREEN_ON_FLAG;
+ mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ + Integer.toHexString(mHistoryCur.states));
mScreenOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin]
.stopRunningLocked(elapsedRealtimeMs);
}
+ updateHistory = true;
}
- if (startStates != 0 || stopStates != 0) {
- mHistory.recordStateChangeEvent(elapsedRealtimeMs, uptimeMs, startStates,
- stopStates);
+ if (updateHistory) {
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
+ + Display.stateToString(state));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
// Per screen state Cpu stats needed. Prepare to schedule an external sync.
@@ -5396,7 +5888,13 @@ public class BatteryStatsImpl extends BatteryStats {
long uptimeMs) {
if (mScreenBrightnessBin != overallBin) {
if (overallBin >= 0) {
- mHistory.recordScreenBrightnessEvent(elapsedRealtimeMs, uptimeMs, overallBin);
+ mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK)
+ | (overallBin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
+ if (DEBUG_HISTORY) {
+ Slog.v(TAG, "Screen brightness " + overallBin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ }
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
if (mScreenState == Display.STATE_ON) {
if (mScreenBrightnessBin >= 0) {
@@ -5424,8 +5922,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWakeUpLocked(String reason, int reasonUid,
long elapsedRealtimeMs, long uptimeMs) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP, reason,
- reasonUid);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_SCREEN_WAKE_UP,
+ reason, reasonUid);
}
@GuardedBy("this")
@@ -5444,7 +5942,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteConnectivityChangedLocked(int type, String extra,
long elapsedRealtimeMs, long uptimeMs) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_CONNECTIVITY_CHANGED,
extra, type);
mNumConnectivityChange++;
}
@@ -5453,7 +5951,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void noteMobileRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
- mHistory.recordEvent(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
+ addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
uid);
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteMobileRadioApWakeupLocked();
}
@@ -5479,8 +5977,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
mMobileRadioActiveStartTimeMs = realElapsedRealtimeMs = timestampNs / (1000 * 1000);
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
} else {
realElapsedRealtimeMs = timestampNs / (1000*1000);
long lastUpdateTimeMs = mMobileRadioActiveStartTimeMs;
@@ -5492,9 +5989,11 @@ public class BatteryStatsImpl extends BatteryStats {
mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtimeMs
- realElapsedRealtimeMs);
}
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
}
+ if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mMobileRadioPowerState = powerState;
// Inform current RatBatteryStats that the modem active state might have changed.
@@ -5544,14 +6043,17 @@ public class BatteryStatsImpl extends BatteryStats {
mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_POWER_SAVE_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_POWER_SAVE_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
@@ -5575,7 +6077,7 @@ public class BatteryStatsImpl extends BatteryStats {
nowLightIdling = true;
}
if (activeReason != null && (mDeviceIdling || mDeviceLightIdling)) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_ACTIVE,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_ACTIVE,
activeReason, activeUid);
}
if (mDeviceIdling != nowIdling || mDeviceLightIdling != nowLightIdling) {
@@ -5605,7 +6107,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
if (mDeviceIdleMode != mode) {
- mHistory.recordDeviceIdleEvent(elapsedRealtimeMs, uptimeMs, mode);
+ mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
+ | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Device idle mode changed to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
long lastDuration = elapsedRealtimeMs - mLastIdleTimeStartMs;
mLastIdleTimeStartMs = elapsedRealtimeMs;
if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) {
@@ -5633,7 +6139,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void notePackageInstalledLocked(String pkgName, long versionCode,
long elapsedRealtimeMs, long uptimeMs) {
// XXX need to figure out what to do with long version codes.
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_INSTALLED,
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_INSTALLED,
pkgName, (int)versionCode);
PackageChange pc = new PackageChange();
pc.mPackageName = pkgName;
@@ -5645,8 +6151,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePackageUninstalledLocked(String pkgName,
long elapsedRealtimeMs, long uptimeMs) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
- pkgName, 0);
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs,
+ HistoryItem.EVENT_PACKAGE_UNINSTALLED, pkgName, 0);
PackageChange pc = new PackageChange();
pc.mPackageName = pkgName;
pc.mUpdate = true;
@@ -5675,8 +6181,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePhoneOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mPhoneOn) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_PHONE_IN_CALL_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mPhoneOn = true;
mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs);
}
@@ -5685,8 +6193,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void notePhoneOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mPhoneOn) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_PHONE_IN_CALL_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_PHONE_IN_CALL_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
@@ -5724,12 +6234,11 @@ public class BatteryStatsImpl extends BatteryStats {
if (mUsbDataState != newState) {
mUsbDataState = newState;
if (connected) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_USB_DATA_LINK_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_USB_DATA_LINK_FLAG;
} else {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_USB_DATA_LINK_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_USB_DATA_LINK_FLAG;
}
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@@ -5750,10 +6259,6 @@ public class BatteryStatsImpl extends BatteryStats {
long elapsedRealtimeMs, long uptimeMs) {
boolean scanning = false;
boolean newHistory = false;
- int addStateFlag = 0;
- int removeStateFlag = 0;
- int newState = -1;
- int newSignalStrength = -1;
mPhoneServiceStateRaw = state;
mPhoneSimStateRaw = simState;
@@ -5782,8 +6287,10 @@ public class BatteryStatsImpl extends BatteryStats {
scanning = true;
strengthBin = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
if (!mPhoneSignalScanningTimer.isRunningLocked()) {
- addStateFlag = HistoryItem.STATE_PHONE_SCANNING_FLAG;
+ mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
newHistory = true;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
+ + Integer.toHexString(mHistoryCur.states));
mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
simState, strengthBin);
@@ -5793,7 +6300,9 @@ public class BatteryStatsImpl extends BatteryStats {
if (!scanning) {
// If we are no longer scanning, then stop the scanning timer.
if (mPhoneSignalScanningTimer.isRunningLocked()) {
- removeStateFlag = HistoryItem.STATE_PHONE_SCANNING_FLAG;
+ mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
+ + Integer.toHexString(mHistoryCur.states));
newHistory = true;
mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
@@ -5802,7 +6311,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mPhoneServiceState != state) {
- newState = state;
+ mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_STATE_MASK)
+ | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + state + " to: "
+ + Integer.toHexString(mHistoryCur.states));
newHistory = true;
mPhoneServiceState = state;
}
@@ -5816,7 +6328,11 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mPhoneSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
}
- newSignalStrength = strengthBin;
+ mHistoryCur.states =
+ (mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
+ | (strengthBin << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
newHistory = true;
FrameworkStatsLog.write(
FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
@@ -5827,8 +6343,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (newHistory) {
- mHistory.recordPhoneStateChangeEvent(elapsedRealtimeMs, uptimeMs,
- addStateFlag, removeStateFlag, newState, newSignalStrength);
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@@ -5952,7 +6467,11 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
- mHistory.recordDataConnectionTypeChangeEvent(elapsedRealtimeMs, uptimeMs, bin);
+ mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
+ | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
elapsedRealtimeMs);
@@ -6025,8 +6544,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mWifiOn) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_WIFI_ON_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiOn = true;
mWifiOnTimer.startRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-off", ExternalStatsSync.UPDATE_WIFI);
@@ -6036,8 +6557,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiOffLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mWifiOn) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_WIFI_ON_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiOn = false;
mWifiOnTimer.stopRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("wifi-on", ExternalStatsSync.UPDATE_WIFI);
@@ -6048,8 +6571,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAudioOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mAudioOnNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_AUDIO_ON_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mAudioOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mAudioOnNesting++;
@@ -6064,8 +6589,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mAudioOnNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_AUDIO_ON_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6076,8 +6603,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteVideoOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mVideoOnNesting == 0) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_VIDEO_ON_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mVideoOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mVideoOnNesting++;
@@ -6092,8 +6621,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mVideoOnNesting == 0) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_VIDEO_ON_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6104,8 +6635,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetAudioLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mAudioOnNesting > 0) {
mAudioOnNesting = 0;
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_AUDIO_ON_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mAudioOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6118,8 +6651,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetVideoLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mVideoOnNesting > 0) {
mVideoOnNesting = 0;
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_VIDEO_ON_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mVideoOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6171,8 +6706,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFlashlightOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mFlashlightOnNesting++ == 0) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_FLASHLIGHT_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6186,8 +6723,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mFlashlightOnNesting == 0) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_FLASHLIGHT_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6198,8 +6737,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteCameraOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mCameraOnNesting++ == 0) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_CAMERA_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_CAMERA_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Camera on to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mCameraOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6213,8 +6754,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (--mCameraOnNesting == 0) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_CAMERA_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6225,8 +6768,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetCameraLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mCameraOnNesting > 0) {
mCameraOnNesting = 0;
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_CAMERA_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mCameraOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6239,8 +6784,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetFlashlightLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mFlashlightOnNesting > 0) {
mFlashlightOnNesting = 0;
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_FLASHLIGHT_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mFlashlightOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6257,8 +6804,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid = mapUid(uid);
if (mBluetoothScanNesting == 0) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mBluetoothScanTimer.startRunningLocked(elapsedRealtimeMs);
}
mBluetoothScanNesting++;
@@ -6299,8 +6848,10 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
mBluetoothScanNesting--;
if (mBluetoothScanNesting == 0) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan stopped for: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mBluetoothScanTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6335,8 +6886,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs, long uptimeMs) {
if (mBluetoothScanNesting > 0) {
mBluetoothScanNesting = 0;
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "BLE can stopped for: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
for (int i=0; i<mUidStats.size(); i++) {
BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
@@ -6376,7 +6929,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void noteWifiRadioApWakeupLocked(final long elapsedRealtimeMillis,
final long uptimeMillis, int uid) {
uid = mapUid(uid);
- mHistory.recordEvent(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
+ addHistoryEventLocked(elapsedRealtimeMillis, uptimeMillis, HistoryItem.EVENT_WAKEUP_AP, "",
uid);
getUidStatsLocked(uid, elapsedRealtimeMillis, uptimeMillis).noteWifiRadioApWakeupLocked();
}
@@ -6392,14 +6945,15 @@ public class BatteryStatsImpl extends BatteryStats {
if (uid > 0) {
noteWifiRadioApWakeupLocked(elapsedRealtimeMs, uptimeMs, uid);
}
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
mWifiActiveTimer.startRunningLocked(elapsedRealtimeMs);
} else {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
mWifiActiveTimer.stopRunningLocked(timestampNs / (1000 * 1000));
}
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mWifiRadioPowerState = powerState;
}
}
@@ -6407,8 +6961,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiRunningLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (!mGlobalWifiRunning) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_WIFI_RUNNING_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_RUNNING_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mGlobalWifiRunning = true;
mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
int N = ws.size();
@@ -6476,8 +7032,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiStoppedLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
if (mGlobalWifiRunning) {
- mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_WIFI_RUNNING_FLAG);
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_RUNNING_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mGlobalWifiRunning = false;
mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtimeMs);
int N = ws.size();
@@ -6525,7 +7083,12 @@ public class BatteryStatsImpl extends BatteryStats {
}
mWifiSupplState = supplState;
mWifiSupplStateTimer[supplState].startRunningLocked(elapsedRealtimeMs);
- mHistory.recordWifiSupplicantStateChangeEvent(elapsedRealtimeMs, uptimeMs, supplState);
+ mHistoryCur.states2 =
+ (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
+ | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wifi suppl state " + supplState + " to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@@ -6554,8 +7117,12 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtimeMs);
}
- mHistory.recordWifiSignalStrengthChangeEvent(elapsedRealtimeMs, uptimeMs,
- strengthBin);
+ mHistoryCur.states2 =
+ (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
+ | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wifi signal strength " + strengthBin + " to: "
+ + Integer.toHexString(mHistoryCur.states2));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
} else {
stopAllWifiSignalStrengthTimersLocked(-1, elapsedRealtimeMs);
}
@@ -6568,8 +7135,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteFullWifiLockAcquiredLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiFullLockNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_FULL_LOCK_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mWifiFullLockNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6580,8 +7149,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockReleasedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiFullLockNesting--;
if (mWifiFullLockNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_FULL_LOCK_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteFullWifiLockReleasedLocked(elapsedRealtimeMs);
@@ -6597,8 +7168,10 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteWifiScanStartedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (mWifiScanNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_SCAN_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
mWifiScanNesting++;
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -6614,8 +7187,10 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
mWifiScanNesting--;
if (mWifiScanNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_SCAN_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteWifiScanStoppedLocked(elapsedRealtimeMs);
@@ -6640,10 +7215,14 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiMulticastEnabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
uid = mapUid(uid);
if (mWifiMulticastNesting == 0) {
- mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG);
+ mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
+
// Start Wifi Multicast overall timer
if (!mWifiMulticastWakelockTimer.isRunningLocked()) {
+ if (DEBUG_HISTORY) Slog.v(TAG, "WiFi Multicast Overall Timer Started");
mWifiMulticastWakelockTimer.startRunningLocked(elapsedRealtimeMs);
}
}
@@ -6657,12 +7236,14 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
mWifiMulticastNesting--;
if (mWifiMulticastNesting == 0) {
- mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG);
+ mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
// Stop Wifi Multicast overall timer
if (mWifiMulticastWakelockTimer.isRunningLocked()) {
- if (DEBUG) Slog.v(TAG, "Multicast Overall Timer Stopped");
+ if (DEBUG_HISTORY) Slog.v(TAG, "Multicast Overall Timer Stopped");
mWifiMulticastWakelockTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
@@ -7414,9 +7995,8 @@ public class BatteryStatsImpl extends BatteryStats {
// If the start clock time has changed by more than a year, then presumably
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
- mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
- currentTimeMs
- );
+ recordCurrentTimeChangeLocked(currentTimeMs, mClock.elapsedRealtime(),
+ mClock.uptimeMillis());
return currentTimeMs - (mClock.elapsedRealtime() - (mRealtimeStartUs / 1000));
}
return mStartClockTimeMs;
@@ -10648,19 +11228,18 @@ public class BatteryStatsImpl extends BatteryStats {
UserInfoProvider userInfoProvider) {
init(clock);
- mHandler = new MyHandler(handler.getLooper());
- mConstants = new Constants(mHandler);
-
if (systemDir == null) {
mStatsFile = null;
- mHistory = new BatteryStatsHistory(mStepDetailsCalculator, mClock);
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
- mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
+ mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer, systemDir,
+ this::getMaxHistoryFiles);
}
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
+ mHandler = new MyHandler(handler.getLooper());
+ mConstants = new Constants(mHandler);
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -10669,6 +11248,7 @@ public class BatteryStatsImpl extends BatteryStats {
initTimes(uptimeUs, realtimeUs);
mStartPlatformVersion = mEndPlatformVersion = Build.ID;
initDischarge(realtimeUs);
+ clearHistoryLocked();
updateDailyDeadlineLocked();
mPlatformIdleStateCallback = cb;
mMeasuredEnergyRetriever = energyStatsCb;
@@ -10679,6 +11259,12 @@ public class BatteryStatsImpl extends BatteryStats {
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
}
+ private int getMaxHistoryFiles() {
+ synchronized (this) {
+ return mConstants.MAX_HISTORY_FILES;
+ }
+ }
+
@VisibleForTesting
protected void initTimersAndCounters() {
mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
@@ -10760,7 +11346,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeUnplugLevel = 0;
mDischargePlugLevel = -1;
mDischargeCurrentLevel = 0;
- mBatteryLevel = 0;
+ mCurrentBatteryLevel = 0;
}
public void setPowerProfileLocked(PowerProfile profile) {
@@ -11147,7 +11733,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public int getHistoryUsedSize() {
- return mHistory.getHistoryUsedSize();
+ return mBatteryStatsHistory.getHistoryUsedSize();
}
@Override
@@ -11161,27 +11747,43 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@VisibleForTesting
public BatteryStatsHistoryIterator createBatteryStatsHistoryIterator() {
- return mHistory.iterate();
+ return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
}
@Override
public int getHistoryStringPoolSize() {
- return mHistory.getHistoryStringPoolSize();
+ return mHistoryTagPool.size();
}
@Override
public int getHistoryStringPoolBytes() {
- return mHistory.getHistoryStringPoolBytes();
+ return mNumHistoryTagChars;
}
@Override
public String getHistoryTagPoolString(int index) {
- return mHistory.getHistoryTagPoolString(index);
+ ensureHistoryTagArray();
+ HistoryTag historyTag = mHistoryTags.get(index);
+ return historyTag != null ? historyTag.string : null;
}
@Override
public int getHistoryTagPoolUid(int index) {
- return mHistory.getHistoryTagPoolUid(index);
+ ensureHistoryTagArray();
+ HistoryTag historyTag = mHistoryTags.get(index);
+ return historyTag != null ? historyTag.uid : Process.INVALID_UID;
+ }
+
+ private void ensureHistoryTagArray() {
+ if (mHistoryTags != null) {
+ return;
+ }
+
+ mHistoryTags = new SparseArray<>(mHistoryTagPool.size());
+ for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) {
+ mHistoryTags.put(entry.getValue() & ~BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG,
+ entry.getKey());
+ }
}
@Override
@@ -11191,11 +11793,15 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public void finishIteratingHistoryLocked() {
- mBatteryStatsHistoryIterator.close();
mBatteryStatsHistoryIterator = null;
}
@Override
+ public long getHistoryBaseTime() {
+ return mHistoryBaseTimeMs;
+ }
+
+ @Override
public int getStartCount() {
return mStartCount;
}
@@ -11248,23 +11854,24 @@ public class BatteryStatsImpl extends BatteryStats {
long realtimeUs = mSecRealtime * 1000;
resetAllStatsLocked(mSecUptime, mSecRealtime, RESET_REASON_ADB_COMMAND);
pullPendingStateUpdatesLocked();
- mHistory.writeHistoryItem(mSecRealtime, mSecUptime);
- mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel = mBatteryLevel;
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
+ mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel
+ = mCurrentBatteryLevel = mHistoryCur.batteryLevel;
mOnBatteryTimeBase.reset(uptimeUs, realtimeUs);
mOnBatteryScreenOffTimeBase.reset(uptimeUs, realtimeUs);
- if (!mBatteryPluggedIn) {
+ if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
if (Display.isOnState(mScreenState)) {
- mDischargeScreenOnUnplugLevel = mBatteryLevel;
+ mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = 0;
} else if (Display.isDozeState(mScreenState)) {
mDischargeScreenOnUnplugLevel = 0;
- mDischargeScreenDozeUnplugLevel = mBatteryLevel;
+ mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
mDischargeScreenDozeUnplugLevel = 0;
- mDischargeScreenOffUnplugLevel = mBatteryLevel;
+ mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
@@ -11408,12 +12015,27 @@ public class BatteryStatsImpl extends BatteryStats {
resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
+ mLastHistoryStepDetails = null;
+ mLastStepCpuUserTimeMs = mLastStepCpuSystemTimeMs = 0;
+ mCurStepCpuUserTimeMs = mCurStepCpuSystemTimeMs = 0;
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
+
mNumAllUidCpuTimeReads = 0;
mNumUidsRemoved = 0;
initDischarge(elapsedRealtimeUs);
- mHistory.reset();
+ clearHistoryLocked();
+ if (mBatteryStatsHistory != null) {
+ mBatteryStatsHistory.resetAllFiles();
+ }
// Flush external data, gathering snapshots, but don't process it since it is pre-reset data
mIgnoreNextExternalStats = true;
@@ -11436,7 +12058,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- mHistory.recordEvent(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
uids.keyAt(j));
}
}
@@ -11861,8 +12483,9 @@ public class BatteryStatsImpl extends BatteryStats {
(long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt);
mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
monitoredRailChargeConsumedMaMs);
- mHistory.recordWifiConsumedCharge(elapsedRealtimeMs, uptimeMs,
- (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
+ mHistoryCur.wifiRailChargeMah +=
+ (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mTmpRailStats.resetWifiTotalEnergyUsed();
if (uidEstimatedConsumptionMah != null) {
@@ -11975,8 +12598,9 @@ public class BatteryStatsImpl extends BatteryStats {
(long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt);
mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
monitoredRailChargeConsumedMaMs);
- mHistory.recordWifiConsumedCharge(elapsedRealtimeMs, uptimeMs,
- (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
+ mHistoryCur.modemRailChargeMah +=
+ (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
mTmpRailStats.resetCellularTotalEnergyUsed();
}
@@ -12244,8 +12868,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
- mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG);
+ mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@@ -13678,7 +14302,11 @@ public class BatteryStatsImpl extends BatteryStats {
mHandler.removeCallbacks(mDeferSetCharging);
if (mCharging != charging) {
mCharging = charging;
- mHistory.setChargingState(charging);
+ if (charging) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
+ } else {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG;
+ }
mHandler.sendEmptyMessage(MSG_REPORT_CHARGING);
return true;
}
@@ -13692,15 +14320,6 @@ public class BatteryStatsImpl extends BatteryStats {
mSystemReady = true;
}
- /**
- * Force recording of all history events regardless of the "charging" state.
- */
- @VisibleForTesting
- public void forceRecordAllHistory() {
- mHistory.forceRecordAllHistory();
- mRecordAllHistory = true;
- }
-
@GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUah) {
@@ -13784,12 +14403,15 @@ public class BatteryStatsImpl extends BatteryStats {
mInitStepMode = mCurStepMode;
mModStepMode = 0;
pullPendingStateUpdatesLocked();
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+ + Integer.toHexString(mHistoryCur.states));
if (reset) {
- mHistory.startRecordingHistory(mSecRealtime, mSecUptime, reset);
- initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
+ mRecordingHistory = true;
+ startRecordingHistory(mSecRealtime, mSecUptime, reset);
}
- mBatteryPluggedIn = false;
- mHistory.recordBatteryState(mSecRealtime, mSecUptime, level, mBatteryPluggedIn);
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
if (Display.isOnState(screenState)) {
mDischargeScreenOnUnplugLevel = level;
@@ -13811,8 +14433,11 @@ public class BatteryStatsImpl extends BatteryStats {
} else {
mOnBattery = mOnBatteryInternal = false;
pullPendingStateUpdatesLocked();
- mBatteryPluggedIn = true;
- mHistory.recordBatteryState(mSecRealtime, mSecUptime, level, mBatteryPluggedIn);
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargePlugLevel = level;
if (level < mDischargeUnplugLevel) {
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
@@ -13827,12 +14452,45 @@ public class BatteryStatsImpl extends BatteryStats {
mModStepMode = 0;
}
if (doWrite || (mLastWriteTimeMs + (60 * 1000)) < mSecRealtime) {
- if (mStatsFile != null && !mHistory.isReadOnly()) {
+ if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
writeAsyncLocked();
}
}
}
+ @GuardedBy("this")
+ private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+ boolean reset) {
+ mRecordingHistory = true;
+ mHistoryCur.currentTime = mClock.currentTimeMillis();
+ addHistoryBufferLocked(elapsedRealtimeMs,
+ reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
+ mHistoryCur);
+ mHistoryCur.currentTime = 0;
+ if (reset) {
+ initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
+ @GuardedBy("this")
+ private void recordCurrentTimeChangeLocked(final long currentTimeMs,
+ final long elapsedRealtimeMs, final long uptimeMs) {
+ if (mRecordingHistory) {
+ mHistoryCur.currentTime = currentTimeMs;
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
+ mHistoryCur.currentTime = 0;
+ }
+ }
+
+ @GuardedBy("this")
+ private void recordShutdownLocked(final long currentTimeMs, final long elapsedRealtimeMs) {
+ if (mRecordingHistory) {
+ mHistoryCur.currentTime = currentTimeMs;
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
+ mHistoryCur.currentTime = 0;
+ }
+ }
+
private void scheduleSyncExternalStatsLocked(String reason, int updateFlags) {
if (mExternalSync != null) {
mExternalSync.scheduleSync(reason, updateFlags);
@@ -13850,7 +14508,8 @@ public class BatteryStatsImpl extends BatteryStats {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
- reportChangesToStatsLog(status, plugType, level);
+ reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
+ status, plugType, level);
final boolean onBattery = isOnBattery(plugType, status);
if (!mHaveBatteryLevel) {
@@ -13860,47 +14519,52 @@ public class BatteryStatsImpl extends BatteryStats {
// plugged in, then twiddle our state to correctly reflect that
// since we won't be going through the full setOnBattery().
if (onBattery == mOnBattery) {
- mHistory.setPluggedInState(!onBattery);
+ if (onBattery) {
+ mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+ } else {
+ mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+ }
}
- mBatteryStatus = status;
- mBatteryLevel = level;
- mBatteryChargeUah = chargeUah;
-
// Always start out assuming charging, that will be updated later.
- mHistory.setBatteryState(true /* charging */, status, level, chargeUah);
-
+ mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
+ mHistoryCur.batteryStatus = (byte)status;
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.batteryChargeUah = chargeUah;
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
- } else if (mBatteryLevel != level || mOnBattery != onBattery) {
+ } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
recordDailyStatsIfNeededLocked(level >= 100 && onBattery, currentTimeMs);
}
- int oldStatus = mBatteryStatus;
+ int oldStatus = mHistoryCur.batteryStatus;
if (onBattery) {
mDischargeCurrentLevel = level;
- if (!mHistory.isRecordingHistory()) {
- mHistory.startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
}
} else if (level < 96 &&
status != BatteryManager.BATTERY_STATUS_UNKNOWN) {
- if (!mHistory.isRecordingHistory()) {
- mHistory.startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
+ if (!mRecordingHistory) {
+ mRecordingHistory = true;
+ startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
}
}
+ mBatteryVoltageMv = voltageMv;
+ mCurrentBatteryLevel = level;
if (mDischargePlugLevel < 0) {
mDischargePlugLevel = level;
}
if (onBattery != mOnBattery) {
- mBatteryLevel = level;
- mBatteryStatus = status;
- mBatteryHealth = health;
- mBatteryPlugType = plugType;
- mBatteryTemperature = temp;
- mBatteryVoltageMv = voltageMv;
- mHistory.setBatteryState(status, level, health, plugType, temp, voltageMv, chargeUah);
- if (chargeUah < mBatteryChargeUah) {
+ mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.batteryStatus = (byte)status;
+ mHistoryCur.batteryHealth = (byte)health;
+ mHistoryCur.batteryPlugType = (byte)plugType;
+ mHistoryCur.batteryTemperature = (short)temp;
+ mHistoryCur.batteryVoltage = (char) voltageMv;
+ if (chargeUah < mHistoryCur.batteryChargeUah) {
// Only record discharges
- final long chargeDiff = (long) mBatteryChargeUah - chargeUah;
+ final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
@@ -13912,12 +14576,12 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
- mBatteryChargeUah = chargeUah;
+ mHistoryCur.batteryChargeUah = chargeUah;
setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUah);
} else {
boolean changed = false;
- if (mBatteryLevel != level) {
- mBatteryLevel = level;
+ if (mHistoryCur.batteryLevel != level) {
+ mHistoryCur.batteryLevel = (byte)level;
changed = true;
// TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
@@ -13925,33 +14589,33 @@ public class BatteryStatsImpl extends BatteryStats {
mExternalSync.scheduleSyncDueToBatteryLevelChange(
mConstants.BATTERY_LEVEL_COLLECTION_DELAY_MS);
}
- if (mBatteryStatus != status) {
- mBatteryStatus = status;
+ if (mHistoryCur.batteryStatus != status) {
+ mHistoryCur.batteryStatus = (byte)status;
changed = true;
}
- if (mBatteryHealth != health) {
- mBatteryHealth = health;
+ if (mHistoryCur.batteryHealth != health) {
+ mHistoryCur.batteryHealth = (byte)health;
changed = true;
}
- if (mBatteryPlugType != plugType) {
- mBatteryPlugType = plugType;
+ if (mHistoryCur.batteryPlugType != plugType) {
+ mHistoryCur.batteryPlugType = (byte)plugType;
changed = true;
}
- if (temp >= (mBatteryTemperature + 10)
- || temp <= (mBatteryTemperature - 10)) {
- mBatteryTemperature = temp;
+ if (temp >= (mHistoryCur.batteryTemperature+10)
+ || temp <= (mHistoryCur.batteryTemperature-10)) {
+ mHistoryCur.batteryTemperature = (short)temp;
changed = true;
}
- if (voltageMv > (mBatteryVoltageMv + 20)
- || voltageMv < (mBatteryVoltageMv - 20)) {
- mBatteryVoltageMv = voltageMv;
+ if (voltageMv > (mHistoryCur.batteryVoltage + 20)
+ || voltageMv < (mHistoryCur.batteryVoltage - 20)) {
+ mHistoryCur.batteryVoltage = (char) voltageMv;
changed = true;
}
- if (chargeUah >= (mBatteryChargeUah + 10)
- || chargeUah <= (mBatteryChargeUah - 10)) {
- if (chargeUah < mBatteryChargeUah) {
+ if (chargeUah >= (mHistoryCur.batteryChargeUah + 10)
+ || chargeUah <= (mHistoryCur.batteryChargeUah - 10)) {
+ if (chargeUah < mHistoryCur.batteryChargeUah) {
// Only record discharges
- final long chargeDiff = (long) mBatteryChargeUah - chargeUah;
+ final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
if (Display.isDozeState(mScreenState)) {
@@ -13963,10 +14627,9 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeDeepDozeCounter.addCountLocked(chargeDiff);
}
}
- mBatteryChargeUah = chargeUah;
+ mHistoryCur.batteryChargeUah = chargeUah;
changed = true;
}
-
long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
| (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
| (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
@@ -14024,10 +14687,7 @@ public class BatteryStatsImpl extends BatteryStats {
mLastChargeStepLevel = level;
}
if (changed) {
- mHistory.setBatteryState(mBatteryStatus, mBatteryLevel, mBatteryHealth,
- mBatteryPlugType, mBatteryTemperature, mBatteryVoltageMv,
- mBatteryChargeUah);
- mHistory.writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
if (!onBattery &&
@@ -14036,7 +14696,7 @@ public class BatteryStatsImpl extends BatteryStats {
// We don't record history while we are plugged in and fully charged
// (or when battery is not present). The next time we are
// unplugged, history will be cleared.
- mHistory.setHistoryRecordingEnabled(DEBUG);
+ mRecordingHistory = DEBUG;
}
mLastLearnedBatteryCapacityUah = chargeFullUah;
@@ -14055,18 +14715,17 @@ public class BatteryStatsImpl extends BatteryStats {
}
// Inform StatsLog of setBatteryState changes.
- private void reportChangesToStatsLog(final int status, final int plugType, final int level) {
- if (!mHaveBatteryLevel) {
- return;
- }
+ // If this is the first reporting, pass in recentPast == null.
+ private void reportChangesToStatsLog(HistoryItem recentPast,
+ final int status, final int plugType, final int level) {
- if (mBatteryStatus != status) {
+ if (recentPast == null || recentPast.batteryStatus != status) {
FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
- if (mBatteryPlugType != plugType) {
+ if (recentPast == null || recentPast.batteryPlugType != plugType) {
FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
- if (mBatteryLevel != level) {
+ if (recentPast == null || recentPast.batteryLevel != level) {
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
@@ -14136,7 +14795,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (msPerLevel <= 0) {
return -1;
}
- return (msPerLevel * mBatteryLevel) * 1000;
+ return (msPerLevel * mCurrentBatteryLevel) * 1000;
}
@Override
@@ -14166,7 +14825,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (msPerLevel <= 0) {
return -1;
}
- return (msPerLevel * (100 - mBatteryLevel)) * 1000;
+ return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
}
/*@hide */
@@ -14597,8 +15256,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void shutdownLocked() {
- mHistory.recordShutdownEvent(mClock.elapsedRealtime(), mClock.uptimeMillis(),
- mClock.currentTimeMillis());
+ recordShutdownLocked(mClock.currentTimeMillis(), mClock.elapsedRealtime());
writeSyncLocked();
mShuttingDown = true;
}
@@ -14806,6 +15464,7 @@ public class BatteryStatsImpl extends BatteryStats {
PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
+
MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
ActivityManager.isLowRamDeviceStatic() ?
DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
@@ -14816,20 +15475,9 @@ public class BatteryStatsImpl extends BatteryStats {
: DEFAULT_MAX_HISTORY_BUFFER_KB)
* 1024;
updateBatteryChargedDelayMsLocked();
-
- onChange();
}
}
- /**
- * Propagates changes in constant values.
- */
- @VisibleForTesting
- public void onChange() {
- mHistory.setMaxHistoryFiles(MAX_HISTORY_FILES);
- mHistory.setMaxHistoryBufferSize(MAX_HISTORY_BUFFER);
- }
-
private void updateBatteryChargedDelayMsLocked() {
// a negative value indicates that we should ignore this override
final int delay = Settings.Global.getInt(mResolver,
@@ -15050,11 +15698,27 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void writeHistoryLocked() {
+ if (mBatteryStatsHistory.getActiveFile() == null) {
+ Slog.w(TAG, "writeHistoryLocked: no history file associated with this instance");
+ return;
+ }
+
if (mShuttingDown) {
return;
}
- mHistory.writeHistory();
+ Parcel p = Parcel.obtain();
+ try {
+ final long start = SystemClock.uptimeMillis();
+ writeHistoryBuffer(p, true);
+ if (DEBUG) {
+ Slog.d(TAG, "writeHistoryBuffer duration ms:"
+ + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
+ }
+ writeParcelToFileLocked(p, mBatteryStatsHistory.getActiveFile());
+ } finally {
+ p.recycle();
+ }
}
private final ReentrantLock mWriteLock = new ReentrantLock();
@@ -15093,6 +15757,13 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
+ final AtomicFile activeHistoryFile = mBatteryStatsHistory.getActiveFile();
+ if (activeHistoryFile == null) {
+ Slog.w(TAG,
+ "readLocked: no history file associated with this instance");
+ return;
+ }
+
mUidStats.clear();
Parcel stats = Parcel.obtain();
@@ -15105,7 +15776,7 @@ public class BatteryStatsImpl extends BatteryStats {
readSummaryFromParcel(stats);
if (DEBUG) {
Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
- + " bytes:" + raw.length + " took ms:" + (SystemClock.uptimeMillis()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- start));
}
}
@@ -15117,19 +15788,126 @@ public class BatteryStatsImpl extends BatteryStats {
stats.recycle();
}
- if (!mHistory.readSummary()) {
- resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
- RESET_REASON_CORRUPT_FILE);
+ Parcel history = Parcel.obtain();
+ try {
+ final long start = SystemClock.uptimeMillis();
+ if (activeHistoryFile.exists()) {
+ byte[] raw = activeHistoryFile.readFully();
+ if (raw.length > 0) {
+ history.unmarshall(raw, 0, raw.length);
+ history.setDataPosition(0);
+ readHistoryBuffer(history);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked history file::"
+ + activeHistoryFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading battery history", e);
+ clearHistoryLocked();
+ mBatteryStatsHistory.resetAllFiles();
+ } finally {
+ history.recycle();
}
mEndPlatformVersion = Build.ID;
- mHistory.continueRecordingHistory();
+ if (mHistoryBuffer.dataPosition() > 0
+ || mBatteryStatsHistory.getFilesNumbers().size() > 1) {
+ mRecordingHistory = true;
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_START, mHistoryCur);
+ startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
+ }
recordDailyStatsIfNeededLocked(false, mClock.currentTimeMillis());
}
@GuardedBy("this")
+ void readHistoryBuffer(Parcel in) throws ParcelFormatException {
+ final int version = in.readInt();
+ if (version != BatteryStatsHistory.VERSION) {
+ Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
+ + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats");
+ return;
+ }
+
+ final long historyBaseTime = in.readLong();
+
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+
+ int bufSize = in.readInt();
+ int curPos = in.dataPosition();
+ if (bufSize >= (mConstants.MAX_HISTORY_BUFFER*100)) {
+ throw new ParcelFormatException("File corrupt: history data buffer too large " +
+ bufSize);
+ } else if ((bufSize&~3) != bufSize) {
+ throw new ParcelFormatException("File corrupt: history data buffer not aligned " +
+ bufSize);
+ } else {
+ if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
+ + " bytes at " + curPos);
+ mHistoryBuffer.appendFrom(in, curPos, bufSize);
+ in.setDataPosition(curPos + bufSize);
+ }
+
+ if (DEBUG_HISTORY) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** OLD mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+ mHistoryBaseTimeMs = historyBaseTime;
+ if (DEBUG_HISTORY) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** NEW mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+
+ // We are just arbitrarily going to insert 1 minute from the sample of
+ // the last run until samples in this run.
+ if (mHistoryBaseTimeMs > 0) {
+ long oldnow = mClock.elapsedRealtime();
+ mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
+ if (DEBUG_HISTORY) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** ADJUSTED mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+ }
+ }
+
+ void writeHistoryBuffer(Parcel out, boolean inclData) {
+ if (DEBUG_HISTORY) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("****************** WRITING mHistoryBaseTimeMs: ");
+ TimeUtils.formatDuration(mHistoryBaseTimeMs, sb);
+ sb.append(" mLastHistoryElapsedRealtimeMs: ");
+ TimeUtils.formatDuration(mLastHistoryElapsedRealtimeMs, sb);
+ Slog.i(TAG, sb.toString());
+ }
+ out.writeInt(BatteryStatsHistory.VERSION);
+ out.writeLong(mHistoryBaseTimeMs + mLastHistoryElapsedRealtimeMs);
+ if (!inclData) {
+ out.writeInt(0);
+ out.writeInt(0);
+ return;
+ }
+
+ out.writeInt(mHistoryBuffer.dataSize());
+ if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+ + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
+ out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+ }
+
+ @GuardedBy("this")
public void readSummaryFromParcel(Parcel in) throws ParcelFormatException {
final int version = in.readInt();
@@ -15139,7 +15917,31 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- mHistory.readSummaryFromParcel(in);
+ boolean inclHistory = in.readBoolean();
+ if (inclHistory) {
+ readHistoryBuffer(in);
+ mBatteryStatsHistory.readFromParcel(in);
+ }
+
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
+
+ int numTags = in.readInt();
+ for (int i=0; i<numTags; i++) {
+ int idx = in.readInt();
+ String str = in.readString();
+ int uid = in.readInt();
+ HistoryTag tag = new HistoryTag();
+ tag.string = str;
+ tag.uid = uid;
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(tag, idx);
+ if (idx >= mNextHistoryTagIdx) {
+ mNextHistoryTagIdx = idx+1;
+ }
+ mNumHistoryTagChars += tag.string.length() + 1;
+ }
mStartCount = in.readInt();
mUptimeUs = in.readLong();
@@ -15152,7 +15954,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
- mBatteryLevel = in.readInt();
+ mCurrentBatteryLevel = in.readInt();
mEstimatedBatteryCapacityMah = in.readInt();
mLastLearnedBatteryCapacityUah = in.readInt();
mMinLearnedBatteryCapacityUah = in.readInt();
@@ -15655,7 +16457,19 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(VERSION);
- mHistory.writeSummaryToParcel(out, inclHistory);
+ out.writeBoolean(inclHistory);
+ if (inclHistory) {
+ writeHistoryBuffer(out, true);
+ mBatteryStatsHistory.writeToParcel(out);
+ }
+
+ out.writeInt(mHistoryTagPool.size());
+ for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
+ HistoryTag tag = ent.getKey();
+ out.writeInt(ent.getValue());
+ out.writeString(tag.string);
+ out.writeInt(tag.uid);
+ }
out.writeInt(mStartCount);
out.writeLong(computeUptime(nowUptime, STATS_SINCE_CHARGED));
@@ -15668,7 +16482,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
- out.writeInt(mBatteryLevel);
+ out.writeInt(mCurrentBatteryLevel);
out.writeInt(mEstimatedBatteryCapacityMah);
out.writeInt(mLastLearnedBatteryCapacityUah);
out.writeInt(mMinLearnedBatteryCapacityUah);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index c36d950b6cf6..0cdd4d101459 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -22,6 +22,7 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Parcel;
import android.os.Process;
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
@@ -31,8 +32,10 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.PowerProfile;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -217,7 +220,18 @@ public class BatteryUsageStatsProvider {
}
BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
- batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.copyHistory());
+
+ // Make a copy of battery history to avoid concurrent modification.
+ Parcel historyBuffer = Parcel.obtain();
+ historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
+ batteryStatsImpl.mHistoryBuffer.dataSize());
+
+ final File systemDir =
+ batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile();
+ final BatteryStatsHistory batteryStatsHistory =
+ new BatteryStatsHistory(historyBuffer, systemDir, null);
+
+ batteryUsageStatsBuilder.setBatteryHistory(batteryStatsHistory);
}
BatteryUsageStats stats = batteryUsageStatsBuilder.build();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7e9474395f0c..765e7f08d201 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3245,7 +3245,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
rootTask.moveToFront(reason, task);
// Report top activity change to tracking services and WM
if (mRootWindowContainer.getTopResumedActivity() == this) {
- mAtmService.setResumedActivityUncheckLocked(this, reason);
+ mAtmService.setLastResumedActivityUncheckLocked(this, reason);
}
return true;
}
@@ -5887,7 +5887,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
try {
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
- configChangeFlags, false /* dontReport */));
+ configChangeFlags, false /* dontReport */,
+ false /* autoEnteringPip */));
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index de3b2a686dde..4003eeb61abe 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3563,7 +3563,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// Continue the pausing process after entering pip.
if (r.isState(PAUSING)) {
r.getTask().schedulePauseActivity(r, false /* userLeaving */,
- false /* pauseImmediately */, "auto-pip");
+ false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip");
}
}
};
@@ -4624,7 +4624,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/** Update AMS states when an activity is resumed. */
- void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
+ void setLastResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final Task task = r.getTask();
if (task.isActivityTypeStandard()) {
if (mCurAppTimeTracker != r.appTimeTracker) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5a1afc49c62b..dc91c1597128 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -789,7 +789,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
-
+ r.lastLaunchTime = SystemClock.uptimeMillis();
r.setProcess(proc);
// Ensure activity is allowed to be resumed after process has set.
@@ -835,8 +835,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
final IActivityClientController activityClientController =
proc.hasEverLaunchedActivity() ? null : mService.mActivityClientController;
r.launchCount++;
- r.lastLaunchTime = SystemClock.uptimeMillis();
- proc.setLastActivityLaunchTime(r.lastLaunchTime);
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
@@ -2085,7 +2083,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
* activity releases the top state and reports back, message about acquiring top state will be
* sent to the new top resumed activity.
*/
- void updateTopResumedActivityIfNeeded() {
+ void updateTopResumedActivityIfNeeded(String reason) {
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) {
@@ -2121,6 +2119,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
mService.updateOomAdj();
}
+ // Update the last resumed activity and focused app when the top resumed activity changed
+ // because the new top resumed activity might be already resumed and thus won't have
+ // activity state change to update the records to AMS.
+ if (mTopResumedActivity != null) {
+ mService.setLastResumedActivityUncheckLocked(mTopResumedActivity, reason);
+ }
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 95169dbd7092..55d6b2fe8226 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -375,6 +375,9 @@ public class AppTransition implements Dump {
final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
int redoLayout = notifyAppTransitionStartingLocked(
+ AppTransition.isKeyguardGoingAwayTransitOld(transit),
+ AppTransition.isKeyguardOccludeTransitOld(transit),
+ topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
topOpeningAnim != null
? topOpeningAnim.getStatusBarTransitionsStartTime()
: SystemClock.uptimeMillis(),
@@ -413,11 +416,8 @@ public class AppTransition implements Dump {
}
void freeze() {
- final boolean keyguardGoingAwayCancelled = mNextAppTransitionRequests.contains(
+ final boolean keyguardGoingAway = mNextAppTransitionRequests.contains(
TRANSIT_KEYGUARD_GOING_AWAY);
- final boolean keyguardOccludedCancelled =
- mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_OCCLUDE)
- || mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_UNOCCLUDE);
// The RemoteAnimationControl didn't register AppTransitionListener and
// only initialized the finish and timeout callback when goodToGo().
@@ -429,7 +429,7 @@ public class AppTransition implements Dump {
mNextAppTransitionRequests.clear();
clear();
setReady();
- notifyAppTransitionCancelledLocked(keyguardGoingAwayCancelled, keyguardOccludedCancelled);
+ notifyAppTransitionCancelledLocked(keyguardGoingAway);
}
private void setAppTransitionState(int state) {
@@ -479,11 +479,9 @@ public class AppTransition implements Dump {
}
}
- private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {
+ private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAway) {
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAwayCancelled,
- keyguardOccludedCancelled);
+ mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAway);
}
}
@@ -493,12 +491,14 @@ public class AppTransition implements Dump {
}
}
- private int notifyAppTransitionStartingLocked(long statusBarAnimationStartTime,
+ private int notifyAppTransitionStartingLocked(boolean keyguardGoingAway,
+ boolean keyguardOcclude, long duration, long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
int redoLayout = 0;
for (int i = 0; i < mListeners.size(); i++) {
- redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(
- statusBarAnimationStartTime, statusBarAnimationDuration);
+ redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+ keyguardOcclude, duration, statusBarAnimationStartTime,
+ statusBarAnimationDuration);
}
return redoLayout;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 4b0005d77e40..44f388b6ed39 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -20,6 +20,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
@@ -91,6 +95,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionFlags;
import android.view.WindowManager.TransitionOldType;
import android.view.WindowManager.TransitionType;
+import android.view.animation.Animation;
import android.window.ITaskFragmentOrganizer;
import com.android.internal.annotations.VisibleForTesting;
@@ -292,6 +297,7 @@ public class AppTransitionController {
final int flags = appTransition.getTransitFlags();
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
+ handleNonAppWindowsInTransition(transit, flags);
appTransition.postAnimationCallback();
appTransition.clear();
} finally {
@@ -1165,6 +1171,30 @@ public class AppTransitionController {
}
}
+ private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) {
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
+ && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
+ && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
+ Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
+ if (anim != null) {
+ anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+ mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
+ }
+ }
+ }
+ if ((transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
+ && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ mDisplayContent.startKeyguardExitOnNonAppWindows(
+ transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
+ }
+ }
+
private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 994f07959f3b..5a2409927ecc 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -17,8 +17,12 @@
package com.android.server.wm;
import android.annotation.UiThread;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Handler;
@@ -305,21 +309,21 @@ class AppWarnings {
private void hideDialogsForPackageUiThread(String name) {
// Hides the "unsupported display" dialog if necessary.
if (mUnsupportedDisplaySizeDialog != null && (name == null || name.equals(
- mUnsupportedDisplaySizeDialog.getPackageName()))) {
+ mUnsupportedDisplaySizeDialog.mPackageName))) {
mUnsupportedDisplaySizeDialog.dismiss();
mUnsupportedDisplaySizeDialog = null;
}
// Hides the "unsupported compile SDK" dialog if necessary.
if (mUnsupportedCompileSdkDialog != null && (name == null || name.equals(
- mUnsupportedCompileSdkDialog.getPackageName()))) {
+ mUnsupportedCompileSdkDialog.mPackageName))) {
mUnsupportedCompileSdkDialog.dismiss();
mUnsupportedCompileSdkDialog = null;
}
// Hides the "deprecated target sdk version" dialog if necessary.
if (mDeprecatedTargetSdkVersionDialog != null && (name == null || name.equals(
- mDeprecatedTargetSdkVersionDialog.getPackageName()))) {
+ mDeprecatedTargetSdkVersionDialog.mPackageName))) {
mDeprecatedTargetSdkVersionDialog.dismiss();
mDeprecatedTargetSdkVersionDialog = null;
}
@@ -431,6 +435,49 @@ class AppWarnings {
}
}
+ static class BaseDialog {
+ final AppWarnings mManager;
+ final String mPackageName;
+ AlertDialog mDialog;
+ private BroadcastReceiver mCloseReceiver;
+
+ BaseDialog(AppWarnings manager, String packageName) {
+ mManager = manager;
+ mPackageName = packageName;
+ }
+
+ @UiThread
+ void show() {
+ if (mDialog == null) return;
+ if (mCloseReceiver == null) {
+ mCloseReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ mManager.mUiHandler.hideDialogsForPackage(mPackageName);
+ }
+ }
+ };
+ mManager.mUiContext.registerReceiver(mCloseReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+ Context.RECEIVER_EXPORTED);
+ }
+ Slog.w(TAG, "Showing " + getClass().getSimpleName() + " for package " + mPackageName);
+ mDialog.show();
+ }
+
+ @UiThread
+ void dismiss() {
+ if (mDialog == null) return;
+ if (mCloseReceiver != null) {
+ mManager.mUiContext.unregisterReceiver(mCloseReceiver);
+ mCloseReceiver = null;
+ }
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+
/**
* Handles messages on the ActivityTaskManagerService thread.
*/
diff --git a/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
index 37244bd5bd06..1a7a9b258400 100644
--- a/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
+++ b/services/core/java/com/android/server/wm/DeprecatedTargetSdkVersionDialog.java
@@ -16,31 +16,23 @@
package com.android.server.wm;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import com.android.internal.R;
import com.android.server.utils.AppInstallerUtil;
-public class DeprecatedTargetSdkVersionDialog {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_ATM;
-
- private final AlertDialog mDialog;
- private final String mPackageName;
+class DeprecatedTargetSdkVersionDialog extends AppWarnings.BaseDialog {
- public DeprecatedTargetSdkVersionDialog(final AppWarnings manager, Context context,
+ DeprecatedTargetSdkVersionDialog(final AppWarnings manager, Context context,
ApplicationInfo appInfo) {
- mPackageName = appInfo.packageName;
+ super(manager, appInfo.packageName);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -75,17 +67,4 @@ public class DeprecatedTargetSdkVersionDialog {
// DO NOT MODIFY. Used by CTS to verify the dialog is displayed.
window.getAttributes().setTitle("DeprecatedTargetSdkVersionDialog");
}
-
- public String getPackageName() {
- return mPackageName;
- }
-
- public void show() {
- Log.w(TAG, "Showing SDK deprecation warning for package " + mPackageName);
- mDialog.show();
- }
-
- public void dismiss() {
- mDialog.dismiss();
- }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c6fa4c1874be..8a34af3b0107 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6598,8 +6598,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
// It is only needed when freezing display in legacy transition.
if (mTransitionController.isShellTransitionsEnabled()) return;
continueUpdateOrientationForDiffOrienLaunchingApp();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2d05f07f31c4..1a34c93f2ad6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -602,8 +602,9 @@ public class DisplayPolicy {
}
@Override
- public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
- long statusBarAnimationDuration) {
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+ boolean keyguardOccluding, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
@@ -615,8 +616,7 @@ public class DisplayPolicy {
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
mHandler.post(mAppTransitionCancelled);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index db79eae1d80c..5b702eac7059 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -161,15 +161,15 @@ public class RecentsAnimationController implements DeathRecipient {
*/
final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
@Override
- public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+ boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
continueDeferredCancel();
return 0;
}
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
continueDeferredCancel();
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 552c6a530544..077f8b55e5e6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -512,7 +512,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void onChildPositionChanged(WindowContainer child) {
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
!mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
- mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ mTaskSupervisor.updateTopResumedActivityIfNeeded("onChildPositionChanged");
}
@Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0332935348e9..384a4d17c752 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -920,7 +920,7 @@ class Task extends TaskFragment {
// If the original state is resumed, there is no state change to update focused app.
// So here makes sure the activity focus is set if it is the top.
if (r.isState(RESUMED) && r == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(r, reason);
+ mAtmService.setLastResumedActivityUncheckLocked(r, reason);
}
}
if (!animate) {
@@ -2433,11 +2433,7 @@ class Task extends TaskFragment {
focusableTask.moveToFront(myReason);
// Top display focused root task is changed, update top resumed activity if needed.
if (rootTask.getTopResumedActivity() != null) {
- mTaskSupervisor.updateTopResumedActivityIfNeeded();
- // Set focused app directly because if the next focused activity is already resumed
- // (e.g. the next top activity is on a different display), there won't have activity
- // state change to update it.
- mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason);
+ mTaskSupervisor.updateTopResumedActivityIfNeeded(reason);
}
return rootTask;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 52bf220a647a..4063cae42b6b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -323,6 +323,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Clear preferred top because the adding focusable task has a higher z-order.
mPreferredTopFocusableRootTask = null;
}
+
+ // Update the top resumed activity because the preferred top focusable task may be changed.
+ mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded("addChildTask");
+
mAtmService.updateSleepIfNeededLocked();
onRootTaskOrderChanged(task);
}
@@ -416,12 +420,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
// Update the top resumed activity because the preferred top focusable task may be changed.
- mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded();
-
- final ActivityRecord r = child.getTopResumedActivity();
- if (r != null && r == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt");
- }
+ mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded("positionChildTaskAt");
if (mChildren.indexOf(child) != oldPosition) {
onRootTaskOrderChanged(child);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 44b5b886a5f6..1b7c6d25f9c6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -460,7 +460,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final ActivityRecord prevR = mResumedActivity;
mResumedActivity = r;
- mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ mTaskSupervisor.updateTopResumedActivityIfNeeded(reason);
if (r == null && prevR.mDisplayContent != null
&& prevR.mDisplayContent.getFocusedRootTask() == null) {
// Only need to notify DWPC when no activity will resume.
@@ -773,9 +773,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason);
}
setResumedActivity(record, reason + " - onActivityStateChanged");
- if (record == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(record, reason);
- }
mTaskSupervisor.mRecentTasks.add(record.getTask());
}
}
@@ -1621,7 +1618,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+ "directly: %s, didAutoPip: %b", prev, didAutoPip);
} else {
- schedulePauseActivity(prev, userLeaving, pauseImmediately, reason);
+ schedulePauseActivity(prev, userLeaving, pauseImmediately,
+ false /* autoEnteringPip */, reason);
}
} else {
mPausingActivity = null;
@@ -1675,7 +1673,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
void schedulePauseActivity(ActivityRecord prev, boolean userLeaving,
- boolean pauseImmediately, String reason) {
+ boolean pauseImmediately, boolean autoEnteringPip, String reason) {
ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
try {
EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
@@ -1683,7 +1681,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.token, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
+ prev.configChangeFlags, pauseImmediately, autoEnteringPip));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 88059e1a0d04..d615583f4d7f 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -49,7 +49,9 @@ import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction;
+import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import java.lang.annotation.Retention;
@@ -68,6 +70,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ActivityTaskManagerService mAtmService;
private final WindowManagerGlobalLock mGlobalLock;
+ private final WindowOrganizerController mWindowOrganizerController;
+
/**
* A Map which manages the relationship between
* {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
@@ -82,9 +86,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
- TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
- mAtmService = atm;
+ TaskFragmentOrganizerController(@NonNull ActivityTaskManagerService atm,
+ @NonNull WindowOrganizerController windowOrganizerController) {
+ mAtmService = requireNonNull(atm);
mGlobalLock = atm.mGlobalLock;
+ mWindowOrganizerController = requireNonNull(windowOrganizerController);
}
/**
@@ -131,6 +137,14 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions =
new SparseArray<>();
+ /**
+ * List of {@link TaskFragmentTransaction#getTransactionToken()} that have been sent to the
+ * organizer. If the transaction is sent during a transition, the
+ * {@link TransitionController} will wait until the transaction is finished.
+ * @see #onTransactionFinished(IBinder)
+ */
+ private final List<IBinder> mRunningTransactions = new ArrayList<>();
+
TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) {
mOrganizer = organizer;
mOrganizerPid = pid;
@@ -176,6 +190,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
taskFragment.removeImmediately();
mOrganizedTaskFragments.remove(taskFragment);
}
+ for (int i = mRunningTransactions.size() - 1; i >= 0; i--) {
+ // Cleanup any running transaction to unblock the current transition.
+ onTransactionFinished(mRunningTransactions.get(i));
+ }
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
}
@@ -320,6 +338,40 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setActivityIntent(activity.intent)
.setActivityToken(activityToken);
}
+
+ void dispatchTransaction(@NonNull TaskFragmentTransaction transaction) {
+ if (transaction.isEmpty()) {
+ return;
+ }
+ try {
+ mOrganizer.onTransactionReady(transaction);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
+ return;
+ }
+ onTransactionStarted(transaction.getTransactionToken());
+ }
+
+ /** Called when the transaction is sent to the organizer. */
+ void onTransactionStarted(@NonNull IBinder transactionToken) {
+ if (!mWindowOrganizerController.getTransitionController().isCollecting()) {
+ return;
+ }
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Defer transition ready for TaskFragmentTransaction=%s", transactionToken);
+ mRunningTransactions.add(transactionToken);
+ mWindowOrganizerController.getTransitionController().deferTransitionReady();
+ }
+
+ /** Called when the transaction is finished. */
+ void onTransactionFinished(@NonNull IBinder transactionToken) {
+ if (!mRunningTransactions.remove(transactionToken)) {
+ return;
+ }
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Continue transition ready for TaskFragmentTransaction=%s", transactionToken);
+ mWindowOrganizerController.getTransitionController().continueTransitionReady();
+ }
}
@Nullable
@@ -336,7 +388,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
- public void registerOrganizer(ITaskFragmentOrganizer organizer) {
+ public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -354,7 +406,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
- public void unregisterOrganizer(ITaskFragmentOrganizer organizer) {
+ public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
validateAndGetState(organizer);
final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid();
@@ -372,8 +424,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
- public void registerRemoteAnimations(ITaskFragmentOrganizer organizer, int taskId,
- RemoteAnimationDefinition definition) {
+ public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId,
+ @NonNull RemoteAnimationDefinition definition) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -398,7 +450,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
- public void unregisterRemoteAnimations(ITaskFragmentOrganizer organizer, int taskId) {
+ public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId) {
final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -416,6 +468,17 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
+ @Override
+ public void onTransactionHandled(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull IBinder transactionToken, @NonNull WindowContainerTransaction wct) {
+ synchronized (mGlobalLock) {
+ // Keep the calling identity to avoid unsecure change.
+ mWindowOrganizerController.applyTransaction(wct);
+ final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+ state.onTransactionFinished(transactionToken);
+ }
+ }
+
/**
* Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
* {@code null} if it doesn't, or if the organizer has activity(ies) embedded in untrusted mode.
@@ -775,13 +838,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
final int organizerNum = mPendingTaskFragmentEvents.size();
for (int i = 0; i < organizerNum; i++) {
- final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
- mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
- dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(mPendingTaskFragmentEvents.keyAt(i));
+ dispatchPendingEvents(state, mPendingTaskFragmentEvents.valueAt(i));
}
}
- void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
+ void dispatchPendingEvents(@NonNull TaskFragmentOrganizerState state,
@NonNull List<PendingTaskFragmentEvent> pendingEvents) {
if (pendingEvents.isEmpty()) {
return;
@@ -817,7 +880,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
if (mTmpTaskSet.add(task)) {
// Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
- PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, state.mOrganizer)
.setTask(task)
.build()));
}
@@ -825,7 +888,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
transaction.addChange(prepareChange(event));
}
mTmpTaskSet.clear();
- dispatchTransactionInfo(organizer, transaction);
+ state.dispatchTransaction(transaction);
pendingEvents.removeAll(candidateEvents);
}
@@ -855,6 +918,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+ final TaskFragmentOrganizerState state = validateAndGetState(organizer);
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
// Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
@@ -862,22 +926,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setTask(taskFragment.getTask())
.build()));
transaction.addChange(prepareChange(event));
- dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
+ state.dispatchTransaction(transaction);
mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
}
- private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
- @NonNull TaskFragmentTransaction transaction) {
- if (transaction.isEmpty()) {
- return;
- }
- try {
- organizer.onTransactionReady(transaction);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
- }
- }
-
@Nullable
private TaskFragmentTransaction.Change prepareChange(
@NonNull PendingTaskFragmentEvent event) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 6e0d411caca4..2d3e437bed60 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -31,7 +31,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -66,6 +72,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -73,6 +80,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.view.animation.Animation;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -1058,9 +1066,36 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
@TransitionType int transit, @TransitionFlags int flags) {
+ if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
+ || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
+ && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
+ && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
+ && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
+ Animation anim = mController.mAtm.mWindowManager.mPolicy
+ .createKeyguardWallpaperExit(
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
+ if (anim != null) {
+ anim.scaleCurrentDuration(
+ mController.mAtm.mWindowManager.getTransitionAnimationScaleLocked());
+ dc.mWallpaperController.startWallpaperAnimation(anim);
+ }
+ }
+ dc.startKeyguardExitOnNonAppWindows(
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
+ (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
+ if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
+ // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
+ // need to call IKeyguardService#keyguardGoingAway here.
+ mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation(
+ SystemClock.uptimeMillis(), 0 /* duration */);
+ }
+ }
if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(
- false /* notify */);
+ true /* keyguardOccludingStarted */);
}
}
@@ -1780,6 +1815,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** This undoes one call to {@link #deferTransitionReady}. */
void continueTransitionReady() {
--mReadyTracker.mDeferReadyDepth;
+ // Apply ready in case it is waiting for the previous defer call.
+ applyReady();
}
/**
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 010c509ddd8a..846aa3e3739a 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -637,9 +637,11 @@ class TransitionController {
}
void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
+ final boolean keyguardGoingAway = info.isKeyguardGoingAway();
for (int i = 0; i < mLegacyListeners.size(); ++i) {
// TODO(shell-transitions): handle (un)occlude transition.
- mLegacyListeners.get(i).onAppTransitionStartingLocked(
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+ false /* keyguardOcclude */, 0 /* durationHint */,
SystemClock.uptimeMillis() + statusBarTransitionDelay,
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
}
@@ -654,7 +656,7 @@ class TransitionController {
void dispatchLegacyAppTransitionCancelled() {
for (int i = 0; i < mLegacyListeners.size(); ++i) {
mLegacyListeners.get(i).onAppTransitionCancelledLocked(
- false /* keyguardGoingAwayCancelled */, false /* keyguardOccludedCancelled */);
+ false /* keyguardGoingAway */);
}
}
diff --git a/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java b/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
index 6a878b983543..f376e8b1f9ed 100644
--- a/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
+++ b/services/core/java/com/android/server/wm/UnsupportedCompileSdkDialog.java
@@ -29,13 +29,11 @@ import android.widget.CheckBox;
import com.android.internal.R;
import com.android.server.utils.AppInstallerUtil;
-public class UnsupportedCompileSdkDialog {
- private final AlertDialog mDialog;
- private final String mPackageName;
+class UnsupportedCompileSdkDialog extends AppWarnings.BaseDialog {
- public UnsupportedCompileSdkDialog(final AppWarnings manager, Context context,
+ UnsupportedCompileSdkDialog(final AppWarnings manager, Context context,
ApplicationInfo appInfo) {
- mPackageName = appInfo.packageName;
+ super(manager, appInfo.packageName);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -72,16 +70,4 @@ public class UnsupportedCompileSdkDialog {
alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
mPackageName, AppWarnings.FLAG_HIDE_COMPILE_SDK, !isChecked));
}
-
- public String getPackageName() {
- return mPackageName;
- }
-
- public void show() {
- mDialog.show();
- }
-
- public void dismiss() {
- mDialog.dismiss();
- }
}
diff --git a/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java b/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
index 4a800c41fdeb..b11c22de4286 100644
--- a/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
+++ b/services/core/java/com/android/server/wm/UnsupportedDisplaySizeDialog.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import com.android.internal.R;
-
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -27,13 +25,13 @@ import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
-public class UnsupportedDisplaySizeDialog {
- private final AlertDialog mDialog;
- private final String mPackageName;
+import com.android.internal.R;
- public UnsupportedDisplaySizeDialog(final AppWarnings manager, Context context,
+class UnsupportedDisplaySizeDialog extends AppWarnings.BaseDialog {
+
+ UnsupportedDisplaySizeDialog(final AppWarnings manager, Context context,
ApplicationInfo appInfo) {
- mPackageName = appInfo.packageName;
+ super(manager, appInfo.packageName);
final PackageManager pm = context.getPackageManager();
final CharSequence label = appInfo.loadSafeLabel(pm,
@@ -63,16 +61,4 @@ public class UnsupportedDisplaySizeDialog {
alwaysShow.setOnCheckedChangeListener((buttonView, isChecked) -> manager.setPackageFlag(
mPackageName, AppWarnings.FLAG_HIDE_DISPLAY_SIZE, !isChecked));
}
-
- public String getPackageName() {
- return mPackageName;
- }
-
- public void show() {
- mDialog.show();
- }
-
- public void dismiss() {
- mDialog.dismiss();
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 4b5b6dad4fc4..a71c3866ba38 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -220,13 +220,9 @@ public abstract class WindowManagerInternal {
/**
* Called when a pending app transition gets cancelled.
*
- * @param keyguardGoingAwayCancelled {@code true} if keyguard going away transition was
- * cancelled.
- * @param keyguardOccludedCancelled {@code true} if keyguard (un)occluded transition was
- * cancelled.
+ * @param keyguardGoingAway true if keyguard going away transition got cancelled.
*/
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {}
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {}
/**
* Called when an app transition is timed out.
@@ -236,6 +232,9 @@ public abstract class WindowManagerInternal {
/**
* Called when an app transition gets started
*
+ * @param keyguardGoingAway true if keyguard going away transition is started.
+ * @param keyguardOccluding true if keyguard (un)occlude transition is started.
+ * @param duration the total duration of the transition
* @param statusBarAnimationStartTime the desired start time for all visual animations in
* the status bar caused by this app transition in uptime millis
* @param statusBarAnimationDuration the duration for all visual animations in the status
@@ -246,7 +245,8 @@ public abstract class WindowManagerInternal {
* {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
*/
- public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+ public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+ boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
return 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 298f93d75e7a..22411bb068a0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -425,6 +425,32 @@ public class WindowManagerService extends IWindowManager.Stub
SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
/**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ *
+ * 0: Runs all keyguard animation as local animation
+ * 1: Only runs keyguard going away animation as remote animation
+ * 2: Runs all keyguard animation as remote animation
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ private static final int sEnableRemoteKeyguardAnimation =
+ SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 2);
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ public static final boolean sEnableRemoteKeyguardGoingAwayAnimation =
+ sEnableRemoteKeyguardAnimation >= 1;
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ public static final boolean sEnableRemoteKeyguardOccludeAnimation =
+ sEnableRemoteKeyguardAnimation >= 2;
+
+ /**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
* when the display has different orientation.
*/
@@ -1092,8 +1118,7 @@ public class WindowManagerService extends IWindowManager.Stub
= new WindowManagerInternal.AppTransitionListener() {
@Override
- public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
- boolean keyguardOccludedCancelled) {
+ public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4f03264b1556..68b1d354272d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -147,7 +147,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
- mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
+ mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm, this);
}
void setWindowManager(WindowManagerService wms) {
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 85671105ec25..9abf107c780a 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -11,7 +11,7 @@ per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com,
# BatteryStats
per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
-per-file Android.bp = file:platform/build/soong:/OWNERS
+per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file com_android_server_Usb* = file:/services/usb/OWNERS
per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
per-file com_android_server_hdmi_* = file:/core/java/android/hardware/hdmi/OWNERS
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 054181dfa728..0305c35b1828 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -671,6 +671,21 @@ class DevicePolicyData {
if (mFactoryResetReason != null) {
pw.print("mFactoryResetReason="); pw.println(mFactoryResetReason);
}
+ if (mDelegationMap.size() != 0) {
+ pw.println("mDelegationMap=");
+ pw.increaseIndent();
+ for (int i = 0; i < mDelegationMap.size(); i++) {
+ List<String> delegationScopes = mDelegationMap.valueAt(i);
+ pw.println(mDelegationMap.keyAt(i) + "[size=" + delegationScopes.size()
+ + "]");
+ pw.increaseIndent();
+ for (int j = 0; j < delegationScopes.size(); j++) {
+ pw.println(j + ": " + delegationScopes.get(j));
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
pw.decreaseIndent();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c1c7bceaebd9..4f67f1d3aa28 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5786,29 +5786,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@VisibleForTesting
public void enforceCallerCanRequestDeviceIdAttestation(CallerIdentity caller)
throws SecurityException {
- /**
- * First check if there's a profile owner because the device could be in COMP mode (where
- * there's a device owner and profile owner on the same device).
- * If the caller is from the work profile, then it must be the PO or the delegate, and
- * it must have the right permission to access device identifiers.
- */
- int callerUserId = caller.getUserId();
- if (hasProfileOwner(callerUserId)) {
- // Make sure that the caller is the profile owner or delegate.
- Preconditions.checkCallAuthorization(canInstallCertificates(caller));
- // Verify that the managed profile is on an organization-owned device (or is affiliated
- // with the device owner user) and as such the profile owner can access Device IDs.
- if (isProfileOwnerOfOrganizationOwnedDevice(callerUserId)
- || isUserAffiliatedWithDevice(callerUserId)) {
- return;
- }
- throw new SecurityException(
- "Profile Owner is not allowed to access Device IDs.");
- }
-
- // If not, fall back to the device owner check.
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization(hasDeviceIdAccessUnchecked(caller.getPackageName(),
+ caller.getUid()));
}
@VisibleForTesting
@@ -5856,7 +5835,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final boolean deviceIdAttestationRequired = attestationUtilsFlags != null;
KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
final String alias = keySpec.getKeystoreAlias();
-
Preconditions.checkStringNotEmpty(alias, "Empty alias provided");
Preconditions.checkArgument(
!deviceIdAttestationRequired || keySpec.getAttestationChallenge() != null,
@@ -8363,6 +8341,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
+ PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
}
+ // TODO(b/240562946): Remove owner name from API parameters.
@Override
public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId,
boolean setProfileOwnerOnCurrentUserIfNecessary) {
@@ -8397,7 +8376,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
}
- mOwners.setDeviceOwner(admin, ownerName, userId);
+ mOwners.setDeviceOwner(admin, userId);
mOwners.writeDeviceOwner();
setDeviceOwnershipSystemPropertyLocked();
@@ -8649,6 +8628,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ // TODO(b/240562946): Remove api as owner name is not used.
/**
* Returns the "name" of the device owner. It'll work for non-DO users too, but requires
* MANAGE_USERS.
@@ -8819,6 +8799,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
+ // TODO(b/240562946): Remove owner name from API parameters.
@Override
public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
@@ -8866,7 +8847,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Shutting down backup manager service permanently.
toggleBackupServiceActive(userHandle, /* makeActive= */ false);
- mOwners.setProfileOwner(who, ownerName, userHandle);
+ mOwners.setProfileOwner(who, userHandle);
mOwners.writeProfileOwner(userHandle);
Slogf.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
@@ -9337,6 +9318,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return who.getPackageName().equals(configPackage);
}
+ // TODO(b/240562946): Remove api as owner name is not used.
@Override
public String getProfileOwnerName(int userHandle) {
if (!mHasFeature) {
@@ -9389,26 +9371,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!hasPermission(permission.READ_PHONE_STATE, pid, uid)) {
return false;
}
+ return hasDeviceIdAccessUnchecked(packageName, uid);
+ }
- // Allow access to the device owner or delegate cert installer or profile owner of an
- // affiliated user
+ /**
+ * Check if caller is device owner, delegate cert installer or profile owner of
+ * affiliated user. Or if caller is profile owner for a specified user or delegate cert
+ * installer on an organization-owned device.
+ */
+ private boolean hasDeviceIdAccessUnchecked(String packageName, int uid) {
+ // Is the caller a device owner, delegate cert installer or profile owner of an
+ // affiliated user.
ComponentName deviceOwner = getDeviceOwnerComponent(true);
if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
|| isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
return true;
}
final int userId = UserHandle.getUserId(uid);
- // Allow access to the profile owner for the specified user, or delegate cert installer
- // But only if this is an organization-owned device.
+ // Is the caller the profile owner for the specified user, or delegate cert installer on an
+ // organization-owned device.
ComponentName profileOwner = getProfileOwnerAsUser(userId);
final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
&& (profileOwner.getPackageName().equals(packageName)
- || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
+ || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
if (isCallerProfileOwnerOrDelegate && (isProfileOwnerOfOrganizationOwnedDevice(userId)
|| isUserAffiliatedWithDevice(userId))) {
return true;
}
-
return false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 08bd3e4c7c47..3b46d5238e6a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -193,12 +193,6 @@ class Owners {
}
}
- String getDeviceOwnerName() {
- synchronized (mData) {
- return mData.mDeviceOwner != null ? mData.mDeviceOwner.name : null;
- }
- }
-
ComponentName getDeviceOwnerComponent() {
synchronized (mData) {
return mData.mDeviceOwner != null ? mData.mDeviceOwner.admin : null;
@@ -217,7 +211,7 @@ class Owners {
}
}
- void setDeviceOwner(ComponentName admin, String ownerName, int userId) {
+ void setDeviceOwner(ComponentName admin, int userId) {
if (userId < 0) {
Slog.e(TAG, "Invalid user id for device owner user: " + userId);
return;
@@ -226,7 +220,7 @@ class Owners {
// A device owner is allowed to access device identifiers. Even though this flag
// is not currently checked for device owner, it is set to true here so that it is
// semantically compatible with the meaning of this flag.
- mData.mDeviceOwner = new OwnerInfo(ownerName, admin, /* remoteBugreportUri =*/ null,
+ mData.mDeviceOwner = new OwnerInfo(admin, /* remoteBugreportUri =*/ null,
/* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ true);
mData.mDeviceOwnerUserId = userId;
@@ -248,10 +242,10 @@ class Owners {
}
}
- void setProfileOwner(ComponentName admin, String ownerName, int userId) {
+ void setProfileOwner(ComponentName admin, int userId) {
synchronized (mData) {
// For a newly set PO, there's no need for migration.
- mData.mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
+ mData.mProfileOwners.put(userId, new OwnerInfo(admin,
/* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null,
/* isOrganizationOwnedDevice =*/ false));
mUserManagerInternal.setUserManaged(userId, true);
@@ -270,7 +264,7 @@ class Owners {
void transferProfileOwner(ComponentName target, int userId) {
synchronized (mData) {
final OwnerInfo ownerInfo = mData.mProfileOwners.get(userId);
- final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
+ final OwnerInfo newOwnerInfo = new OwnerInfo(target,
ownerInfo.remoteBugreportUri, ownerInfo.remoteBugreportHash,
ownerInfo.isOrganizationOwnedDevice);
mData.mProfileOwners.put(userId, newOwnerInfo);
@@ -282,9 +276,7 @@ class Owners {
synchronized (mData) {
Integer previousDeviceOwnerType = mData.mDeviceOwnerTypes.remove(
mData.mDeviceOwner.packageName);
- // We don't set a name because it's not used anyway.
- // See DevicePolicyManagerService#getDeviceOwnerName
- mData.mDeviceOwner = new OwnerInfo(null, target,
+ mData.mDeviceOwner = new OwnerInfo(target,
mData.mDeviceOwner.remoteBugreportUri,
mData.mDeviceOwner.remoteBugreportHash,
mData.mDeviceOwner.isOrganizationOwnedDevice);
@@ -305,13 +297,6 @@ class Owners {
}
}
- String getProfileOwnerName(int userId) {
- synchronized (mData) {
- OwnerInfo profileOwner = mData.mProfileOwners.get(userId);
- return profileOwner != null ? profileOwner.name : null;
- }
- }
-
String getProfileOwnerPackage(int userId) {
synchronized (mData) {
OwnerInfo profileOwner = mData.mProfileOwners.get(userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 694842034d1f..2ab54644fd18 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -493,16 +493,14 @@ class OwnersData {
}
static class OwnerInfo {
- public final String name;
public final String packageName;
public final ComponentName admin;
public String remoteBugreportUri;
public String remoteBugreportHash;
public boolean isOrganizationOwnedDevice;
- OwnerInfo(String name, ComponentName admin, String remoteBugreportUri,
+ OwnerInfo(ComponentName admin, String remoteBugreportUri,
String remoteBugreportHash, boolean isOrganizationOwnedDevice) {
- this.name = name;
this.admin = admin;
this.packageName = admin.getPackageName();
this.remoteBugreportUri = remoteBugreportUri;
@@ -512,9 +510,6 @@ class OwnersData {
public void writeToXml(TypedXmlSerializer out, String tag) throws IOException {
out.startTag(null, tag);
- if (name != null) {
- out.attribute(null, ATTR_NAME, name);
- }
if (admin != null) {
out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString());
}
@@ -532,7 +527,6 @@ class OwnersData {
}
public static OwnerInfo readFromXml(TypedXmlPullParser parser) {
- final String name = parser.getAttributeValue(null, ATTR_NAME);
final String componentName = parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
final String remoteBugreportUri =
parser.getAttributeValue(null, ATTR_REMOTE_BUGREPORT_URI);
@@ -556,13 +550,11 @@ class OwnersData {
return null;
}
- return new OwnerInfo(
- name, admin, remoteBugreportUri, remoteBugreportHash, isOrgOwnedDevice);
+ return new OwnerInfo(admin, remoteBugreportUri, remoteBugreportHash, isOrgOwnedDevice);
}
public void dump(IndentingPrintWriter pw) {
pw.println("admin=" + admin);
- pw.println("name=" + name);
pw.println("package=" + packageName);
pw.println("isOrganizationOwnedDevice=" + isOrganizationOwnedDevice);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 1b39add3b421..9d039bd09a2f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -209,7 +209,8 @@ public class FlexibilityControllerTest {
public void testOnConstantsUpdated_DeadlineProximity() {
JobStatus js = createJobStatus("testDeadlineProximityConfig", createJob(0));
setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, Long.MAX_VALUE);
- mFlexibilityController.mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ mFlexibilityController.mFlexibilityAlarmQueue
+ .scheduleDropNumConstraintsAlarm(js, FROZEN_TIME);
assertEquals(0, js.getNumRequiredFlexibleConstraints());
}
@@ -336,26 +337,31 @@ public class FlexibilityControllerTest {
@Test
public void testCurPercent() {
long deadline = 1000;
+ long nowElapsed;
JobInfo.Builder jb = createJob(0).setOverrideDeadline(deadline);
JobStatus js = createJobStatus("time", jb);
assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
assertEquals(deadline + FROZEN_TIME,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, FROZEN_TIME));
+ nowElapsed = 600 + FROZEN_TIME;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(600 + FROZEN_TIME), ZoneOffset.UTC);
- assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
+ assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed));
+ nowElapsed = 1400;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(1400), ZoneOffset.UTC);
- assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
+ assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed));
+ nowElapsed = 950 + FROZEN_TIME;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(950 + FROZEN_TIME), ZoneOffset.UTC);
- assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
+ assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed));
+ nowElapsed = FROZEN_TIME;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
long delay = 100;
deadline = 1100;
jb = createJob(0).setOverrideDeadline(deadline).setMinimumLatency(delay);
@@ -366,18 +372,21 @@ public class FlexibilityControllerTest {
assertEquals(deadline + FROZEN_TIME,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, FROZEN_TIME + delay));
+ nowElapsed = 600 + FROZEN_TIME + delay;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(600 + FROZEN_TIME + delay), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed));
+ nowElapsed = 1400;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(1400), ZoneOffset.UTC);
- assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
+ assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed));
+ nowElapsed = 950 + FROZEN_TIME + delay;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(950 + FROZEN_TIME + delay), ZoneOffset.UTC);
- assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
+ assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed));
}
@Test
@@ -491,19 +500,19 @@ public class FlexibilityControllerTest {
assertEquals(3, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1);
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
assertEquals(0, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(2, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1);
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(2, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -1);
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -1, FROZEN_TIME);
assertEquals(0, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(2, trackedJobs.get(2).size());
@@ -515,24 +524,25 @@ public class FlexibilityControllerTest {
assertEquals(1, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
- flexTracker.resetJobNumDroppedConstraints(jobs[0]);
+ flexTracker.resetJobNumDroppedConstraints(jobs[0], FROZEN_TIME);
assertEquals(0, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(2, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
- flexTracker.adjustJobsRequiredConstraints(jobs[0], -2);
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -2, FROZEN_TIME);
assertEquals(1, trackedJobs.get(0).size());
assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
+ final long nowElapsed = ((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
+ + HOUR_IN_MILLIS);
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
- + HOUR_IN_MILLIS), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- flexTracker.resetJobNumDroppedConstraints(jobs[0]);
+ flexTracker.resetJobNumDroppedConstraints(jobs[0], nowElapsed);
assertEquals(0, trackedJobs.get(0).size());
assertEquals(1, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
@@ -615,13 +625,13 @@ public class FlexibilityControllerTest {
@Test
public void testSetConstraintSatisfied_Constraints() {
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false);
+ mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME);
assertFalse(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE));
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, true);
+ mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, true, FROZEN_TIME);
assertTrue(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE));
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false);
+ mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME);
assertFalse(mFlexibilityController.isConstraintSatisfied(CONSTRAINT_IDLE));
}
@@ -651,20 +661,21 @@ public class FlexibilityControllerTest {
createJobStatus(String.valueOf(i), jb), null);
}
}
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, false);
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false);
- mFlexibilityController.setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, false);
+ mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING, false, FROZEN_TIME);
+ mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE, false, FROZEN_TIME);
+ mFlexibilityController.setConstraintSatisfied(
+ CONSTRAINT_BATTERY_NOT_LOW, false, FROZEN_TIME);
assertEquals(0, mFlexibilityController.mSatisfiedFlexibleConstraints);
for (int i = 0; i < constraintCombinations.length; i++) {
constraints = constraintCombinations[i];
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING,
- (constraints & CONSTRAINT_CHARGING) != 0);
+ (constraints & CONSTRAINT_CHARGING) != 0, FROZEN_TIME);
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE,
- (constraints & CONSTRAINT_IDLE) != 0);
+ (constraints & CONSTRAINT_IDLE) != 0, FROZEN_TIME);
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW,
- (constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0);
+ (constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0, FROZEN_TIME);
assertEquals(constraints, mFlexibilityController.mSatisfiedFlexibleConstraints);
synchronized (mFlexibilityController.mLock) {
@@ -679,6 +690,7 @@ public class FlexibilityControllerTest {
JobInfo.Builder jb = createJob(22).setOverrideDeadline(100L);
JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);
js.adjustNumRequiredFlexibleConstraints(3);
+ long nowElapsed;
mFlexibilityController.mFlexibilityTracker.add(js);
@@ -687,45 +699,51 @@ public class FlexibilityControllerTest {
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+
+ nowElapsed = 155L;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(155L), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1);
+ mFlexibilityController.mFlexibilityTracker
+ .adjustJobsRequiredConstraints(js, -1, nowElapsed);
assertEquals(2, js.getNumRequiredFlexibleConstraints());
assertEquals(1, js.getNumDroppedFlexibleConstraints());
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
assertEquals(2, js.getNumRequiredFlexibleConstraints());
assertEquals(1, js.getNumDroppedFlexibleConstraints());
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
+ nowElapsed = 140L;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(140L), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
assertEquals(3, js.getNumRequiredFlexibleConstraints());
assertEquals(0, js.getNumDroppedFlexibleConstraints());
assertEquals(1, mFlexibilityController
.mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+ nowElapsed = 175L;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(175), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
assertEquals(0, js.getNumRequiredFlexibleConstraints());
assertEquals(3, js.getNumDroppedFlexibleConstraints());
+ nowElapsed = 165L;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(165L), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
- mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
assertEquals(1, js.getNumRequiredFlexibleConstraints());
assertEquals(2, js.getNumDroppedFlexibleConstraints());
@@ -745,12 +763,14 @@ public class FlexibilityControllerTest {
mFlexibilityController.maybeStartTrackingJobLocked(js, null);
+ final long nowElapsed = 150L;
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(150L), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC);
mFlexibilityController.mPrefetchChangedListener.onPrefetchCacheUpdated(
jobs, js.getUserId(), js.getSourcePackageName(), Long.MAX_VALUE,
- 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS);
+ 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS,
+ nowElapsed);
assertEquals(150L,
(long) mFlexibilityController.mPrefetchLifeCycleStart
@@ -758,7 +778,7 @@ public class FlexibilityControllerTest {
assertEquals(150L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
assertEquals(1150L,
mFlexibilityController.getLifeCycleEndElapsedLocked(js, 150L));
- assertEquals(0, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ assertEquals(0, mFlexibilityController.getCurPercentOfLifecycleLocked(js, FROZEN_TIME));
assertEquals(650L, mFlexibilityController
.getNextConstraintDropTimeElapsedLocked(js));
assertEquals(3, js.getNumRequiredFlexibleConstraints());
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 969389c27e70..bb477b18d5fa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -490,9 +490,9 @@ public class PrefetchControllerTest {
final PrefetchController.PrefetchChangedListener prefetchChangedListener =
new PrefetchController.PrefetchChangedListener() {
@Override
- public void onPrefetchCacheUpdated(
- ArraySet<JobStatus> jobs, int userId, String pkgName,
- long prevEstimatedLaunchTime, long newEstimatedLaunchTime) {
+ public void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs,
+ int userId, String pkgName, long prevEstimatedLaunchTime,
+ long newEstimatedLaunchTime, long nowElapsed) {
onPrefetchCacheChangedCalled[0] = true;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
new file mode 100644
index 000000000000..903ed9082481
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.face.IFaceService;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FaceServiceRegistryTest {
+
+ private static final int SENSOR_ID_1 = 1;
+ private static final int SENSOR_ID_2 = 2;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private IFaceService mFaceService;
+ @Mock
+ private ServiceProvider mProvider1;
+ @Mock
+ private ServiceProvider mProvider2;
+ @Captor
+ private ArgumentCaptor<Integer> mIdCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mStrengthCaptor;
+
+ private FaceSensorPropertiesInternal mProvider1Props;
+ private FaceSensorPropertiesInternal mProvider2Props;
+ private FaceServiceRegistry mRegistry;
+
+ @Before
+ public void setup() {
+ mProvider1Props = new FaceSensorPropertiesInternal(SENSOR_ID_1,
+ STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FaceSensorProperties.TYPE_RGB,
+ true /* supportsFace Detection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mProvider2Props = new FaceSensorPropertiesInternal(SENSOR_ID_2,
+ STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FaceSensorProperties.TYPE_IR,
+ true /* supportsFace Detection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+
+ when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props));
+ when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true);
+ when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props));
+ when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true);
+ mRegistry = new FaceServiceRegistry(mFaceService, () -> mBiometricService);
+ }
+
+ @Test
+ public void registersAllProviders() throws Exception {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2);
+ assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props);
+ verify(mBiometricService, times(2)).registerAuthenticator(
+ mIdCaptor.capture(), eq(TYPE_FACE), mStrengthCaptor.capture(), any());
+ assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2);
+ assertThat(mStrengthCaptor.getAllValues())
+ .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void getsProviderById() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2);
+ assertThat(mRegistry.getProviderForSensor(500)).isNull();
+ }
+
+ @Test
+ public void getsSingleProvider() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1));
+
+ assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ }
+
+ @Test
+ public void getSingleProviderFindsFirstWhenMultiple() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1);
+ }
+
+ @Test
+ public void registersListenerBeforeAllRegistered() {
+ final List<FaceSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+
+ @Test
+ public void registersListenerAfterAllRegistered() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ final List<FaceSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
index 5f88c99b1d1e..0e30782eaece 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -24,38 +26,73 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricStateListener;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricServiceProvider;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
@Presubmit
@SmallTest
public class BiometricStateCallbackTest {
- private BiometricStateCallback mCallback;
+ private static final int USER_ID = 10;
+ private static final int SENSOR_ID = 2;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private BiometricStateCallback<FakeProvider, SensorPropertiesInternal> mCallback;
@Mock
- BiometricStateListener mBiometricStateListener;
+ private UserManager mUserManager;
+ @Mock
+ private BiometricStateListener mBiometricStateListener;
+ @Mock
+ private FakeProvider mFakeProvider;
+
+ private SensorPropertiesInternal mFakeProviderProps;
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mCallback = new BiometricStateCallback();
+ mFakeProviderProps = new SensorPropertiesInternal(SENSOR_ID, STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */, List.of(),
+ false /* resetLockoutRequiresHardwareAuthToken */,
+ false /* resetLockoutRequiresChallenge */);
+ when(mFakeProvider.getSensorProperties()).thenReturn(List.of(mFakeProviderProps));
+ when(mFakeProvider.getSensorProperties(eq(SENSOR_ID))).thenReturn(mFakeProviderProps);
+ when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true);
+ when(mUserManager.getAliveUsers()).thenReturn(
+ List.of(new UserInfo(USER_ID, "name", 0)));
+
+ mCallback = new BiometricStateCallback<>(mUserManager);
mCallback.registerBiometricStateListener(mBiometricStateListener);
}
@Test
+ public void startNotifiesEnrollments() {
+ mCallback.start(List.of(mFakeProvider));
+
+ verify(mBiometricStateListener).onEnrollmentsChanged(eq(USER_ID), eq(SENSOR_ID), eq(true));
+ }
+
+ @Test
public void testNoEnrollmentsToEnrollments_callbackNotified() {
testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */,
true /* expectCallback */, true /* expectedCallbackValue */);
@@ -102,4 +139,6 @@ public class BiometricStateCallbackTest {
verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
anyBoolean());
}
+
+ private interface FakeProvider extends BiometricServiceProvider<SensorPropertiesInternal> {}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
new file mode 100644
index 000000000000..13c3f64fec93
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FingerprintServiceRegistryTest {
+
+ private static final int SENSOR_ID_1 = 1;
+ private static final int SENSOR_ID_2 = 2;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private IFingerprintService mFingerprintService;
+ @Mock
+ private ServiceProvider mProvider1;
+ @Mock
+ private ServiceProvider mProvider2;
+ @Captor
+ private ArgumentCaptor<Integer> mIdCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mStrengthCaptor;
+
+ private FingerprintSensorPropertiesInternal mProvider1Props;
+ private FingerprintSensorPropertiesInternal mProvider2Props;
+ private FingerprintServiceRegistry mRegistry;
+
+ @Before
+ public void setup() {
+ mProvider1Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_1,
+ STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mProvider2Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_2,
+ STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FingerprintSensorProperties.TYPE_UNKNOWN,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+
+ when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props));
+ when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true);
+ when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props));
+ when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true);
+ mRegistry = new FingerprintServiceRegistry(mFingerprintService, () -> mBiometricService);
+ }
+
+ @Test
+ public void registersAllProviders() throws Exception {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2);
+ assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props);
+ verify(mBiometricService, times(2)).registerAuthenticator(
+ mIdCaptor.capture(), eq(TYPE_FINGERPRINT), mStrengthCaptor.capture(), any());
+ assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2);
+ assertThat(mStrengthCaptor.getAllValues())
+ .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void getsProviderById() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2);
+ assertThat(mRegistry.getProviderForSensor(500)).isNull();
+ }
+
+ @Test
+ public void getsSingleProvider() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1));
+
+ assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ }
+
+ @Test
+ public void getSingleProviderFindsFirstWhenMultiple() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1);
+ }
+
+ @Test
+ public void registersListenerBeforeAllRegistered() {
+ final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+
+ @Test
+ public void registersListenerAfterAllRegistered() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index ca3677e3b5ff..a4048a27dfb5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -32,7 +32,8 @@ import android.hardware.biometrics.fingerprint.FingerprintSensorType;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.SensorProps;
-import android.os.RemoteException;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.testing.TestableContext;
@@ -52,6 +53,8 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@Presubmit
@SmallTest
@@ -94,9 +97,12 @@ public class FingerprintServiceTest {
mContext.getTestablePermissions().setPermission(
USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED);
+ }
+ private void initServiceWith(String... aidlInstances) {
mService = new FingerprintService(mContext, mBiometricContext,
() -> mIBiometricService,
+ () -> aidlInstances,
(fqName) -> {
if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault;
if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual;
@@ -105,29 +111,50 @@ public class FingerprintServiceTest {
}
@Test
- public void registerAuthenticators_defaultOnly() throws RemoteException {
- mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of());
+ public void registerAuthenticators_defaultOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
}
@Test
- public void registerAuthenticators_virtualOnly() throws RemoteException {
+ public void registerAuthenticators_virtualOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
- mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of());
+ mService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
}
@Test
- public void registerAuthenticators_virtualAlwaysWhenNoOther() throws RemoteException {
- mService.registerAuthenticatorsForService(List.of(NAME_VIRTUAL), List.of());
+ public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
+ initServiceWith(NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
}
+ private void waitForRegistration() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ latch.countDown();
+ }
+ });
+ latch.await(5, TimeUnit.SECONDS);
+ }
+
private static SensorProps createProps(int id, byte strength, byte type) {
final SensorProps props = new SensorProps();
props.commonProps = new CommonProps();
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 34b40c716b4f..b1ad8ec1cb66 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -16,53 +16,79 @@
package com.android.server.pm;
+import static android.os.UserManager.DISALLOW_USER_SWITCH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.app.PropertyInvalidatedCache;
+import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.FileUtils;
+import android.os.Looper;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
import android.support.test.uiautomator.UiDevice;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
import android.util.AtomicFile;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
+/** Test {@link UserManagerService} functionality. */
@Postsubmit
-@SmallTest
-public class UserManagerServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class UserManagerServiceTest {
private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
private File restrictionsFile;
private int tempUserId = UserHandle.USER_NULL;
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private UserManagerService mUserManagerService;
+
+ @Before
+ public void setup() throws Exception {
+ // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup
+ // TODO: Remove once UMS supports proper dependency injection
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ // Disable binder caches in this process.
+ PropertyInvalidatedCache.disableForTestMode();
+
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());
- @Override
- protected void setUp() throws Exception {
- super.setUp();
restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
restrictionsFile.delete();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void teardown() throws Exception {
restrictionsFile.delete();
if (tempUserId != UserHandle.USER_NULL) {
UserManager.get(mContext).removeUser(tempUserId);
}
- super.tearDown();
}
+ @Test
public void testWriteReadApplicationRestrictions() throws IOException {
AtomicFile atomicFile = new AtomicFile(restrictionsFile);
Bundle bundle = createBundle();
UserManagerService.writeApplicationRestrictionsLAr(bundle, atomicFile);
- assertTrue(atomicFile.getBaseFile().exists());
+ assertThat(atomicFile.getBaseFile().exists()).isTrue();
String s = FileUtils.readTextFile(restrictionsFile, 10000, "");
System.out.println("restrictionsFile: " + s);
bundle = UserManagerService.readApplicationRestrictionsLAr(atomicFile);
@@ -70,22 +96,22 @@ public class UserManagerServiceTest extends AndroidTestCase {
assertBundle(bundle);
}
+ @Test
public void testAddUserWithAccount() {
UserManager um = UserManager.get(mContext);
UserInfo user = um.createUser("Test User", 0);
- assertNotNull(user);
+ assertThat(user).isNotNull();
tempUserId = user.id;
String accountName = "Test Account";
um.setUserAccount(tempUserId, accountName);
- assertEquals(accountName, um.getUserAccount(tempUserId));
+ assertThat(um.getUserAccount(tempUserId)).isEqualTo(accountName);
}
+ @Test
public void testUserSystemPackageWhitelist() throws Exception {
String cmd = "cmd user report-system-user-package-whitelist-problems --critical-only";
final String result = runShellCommand(cmd);
- if (!TextUtils.isEmpty(result)) {
- fail("Command '" + cmd + " reported errors:\n" + result);
- }
+ assertThat(result).isEmpty();
}
private Bundle createBundle() {
@@ -114,26 +140,141 @@ public class UserManagerServiceTest extends AndroidTestCase {
}
private void assertBundle(Bundle bundle) {
- assertFalse(bundle.getBoolean("boolean_0"));
- assertTrue(bundle.getBoolean("boolean_1"));
- assertEquals(100, bundle.getInt("integer"));
- assertEquals("", bundle.getString("empty"));
- assertEquals("text", bundle.getString("string"));
- assertEquals(Arrays.asList(STRING_ARRAY), Arrays.asList(bundle.getStringArray("string[]")));
+ assertThat(bundle.getBoolean("boolean_0")).isFalse();
+ assertThat(bundle.getBoolean("boolean_1")).isTrue();
+ assertThat(bundle.getInt("integer")).isEqualTo(100);
+ assertThat(bundle.getString("empty")).isEqualTo("");
+ assertThat(bundle.getString("string")).isEqualTo("text");
+ assertThat(Arrays.asList(bundle.getStringArray("string[]")))
+ .isEqualTo(Arrays.asList(STRING_ARRAY));
Parcelable[] bundle_array = bundle.getParcelableArray("bundle_array");
- assertEquals(2, bundle_array.length);
+ assertThat(bundle_array.length).isEqualTo(2);
Bundle bundle1 = (Bundle) bundle_array[0];
- assertEquals("bundle_array_string", bundle1.getString("bundle_array_string"));
- assertNotNull(bundle1.getBundle("bundle_array_bundle"));
+ assertThat(bundle1.getString("bundle_array_string"))
+ .isEqualTo("bundle_array_string");
+ assertThat(bundle1.getBundle("bundle_array_bundle")).isNotNull();
Bundle bundle2 = (Bundle) bundle_array[1];
- assertEquals("bundle_array_string2", bundle2.getString("bundle_array_string2"));
+ assertThat(bundle2.getString("bundle_array_string2"))
+ .isEqualTo("bundle_array_string2");
Bundle childBundle = bundle.getBundle("bundle");
- assertEquals("bundle_string", childBundle.getString("bundle_string"));
- assertEquals(1, childBundle.getInt("bundle_int"));
+ assertThat(childBundle.getString("bundle_string"))
+ .isEqualTo("bundle_string");
+ assertThat(childBundle.getInt("bundle_int")).isEqualTo(1);
+ }
+
+ @Test
+ public void assertHasUserRestriction() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+
+ mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
+ assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, userId)).isTrue();
+
+ mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
+ assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, userId)).isFalse();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnMultiUserSettings() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ resetUserSwitcherEnabled();
+
+ setUserSwitch(false);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+ setUserSwitch(true);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnMaxSupportedUsers() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ setMaxSupportedUsers(1);
+
+ assertThat(UserManager.supportsMultipleUsers()).isFalse();
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+ setMaxSupportedUsers(8);
+
+ assertThat(UserManager.supportsMultipleUsers()).isTrue();
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+ }
+
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnShowMultiuserUI() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ setShowMultiuserUI(false);
+
+ assertThat(UserManager.supportsMultipleUsers()).isFalse();
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+ setShowMultiuserUI(true);
+
+ assertThat(UserManager.supportsMultipleUsers()).isTrue();
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnUserRestrictions() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ resetUserSwitcherEnabled();
+
+ mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+ mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+ }
+
+ @Test
+ public void assertIsUserSwitcherEnabledOnDemoMode() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ resetUserSwitcherEnabled();
+
+ setDeviceDemoMode(true);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+ setDeviceDemoMode(false);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+ }
+
+ private void resetUserSwitcherEnabled() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ setUserSwitch(true);
+ setShowMultiuserUI(true);
+ setDeviceDemoMode(false);
+ setMaxSupportedUsers(8);
+ mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
+ }
+
+ private void setUserSwitch(boolean enabled) {
+ android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.USER_SWITCHER_ENABLED, enabled ? 1 : 0);
}
+ private void setDeviceDemoMode(boolean enabled) {
+ android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+ android.provider.Settings.Global.DEVICE_DEMO_MODE, enabled ? 1 : 0);
+ }
+
+
private static String runShellCommand(String cmd) throws Exception {
return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
.executeShellCommand(cmd);
}
+
+ private static String setSystemProperty(String name, String value) throws Exception {
+ final String oldValue = runShellCommand("getprop " + name);
+ assertThat(runShellCommand("setprop " + name + " " + value))
+ .isEqualTo("");
+ return oldValue;
+ }
+
+ private static void setMaxSupportedUsers(int max) throws Exception {
+ setSystemProperty("fw.max_users", String.valueOf(max));
+ }
+
+ public static void setShowMultiuserUI(boolean show) throws Exception {
+ setSystemProperty("fw.show_multiuserui", String.valueOf(show));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 5c9348525861..61a7f3853746 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -28,7 +28,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BatteryStatsHistory;
-import com.android.internal.os.Clock;
import org.junit.Before;
import org.junit.Test;
@@ -50,14 +49,13 @@ public class BatteryStatsHistoryTest {
private final Parcel mHistoryBuffer = Parcel.obtain();
private File mSystemDir;
private File mHistoryDir;
- private final Clock mClock = new MockClock();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext();
mSystemDir = context.getDataDir();
- mHistoryDir = new File(mSystemDir, "battery-history");
+ mHistoryDir = new File(mSystemDir, BatteryStatsHistory.HISTORY_DIR);
String[] files = mHistoryDir.list();
if (files != null) {
for (int i = 0; i < files.length; i++) {
@@ -69,8 +67,8 @@ public class BatteryStatsHistoryTest {
@Test
public void testConstruct() {
- BatteryStatsHistory history = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- null, mClock);
+ BatteryStatsHistory history =
+ new BatteryStatsHistory(mHistoryBuffer, mSystemDir, () -> 32);
createActiveFile(history);
verifyFileNumbers(history, Arrays.asList(0));
verifyActiveFile(history, "0.bin");
@@ -78,8 +76,8 @@ public class BatteryStatsHistoryTest {
@Test
public void testStartNextFile() {
- BatteryStatsHistory history = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- null, mClock);
+ BatteryStatsHistory history =
+ new BatteryStatsHistory(mHistoryBuffer, mSystemDir, () -> 32);
List<Integer> fileList = new ArrayList<>();
fileList.add(0);
createActiveFile(history);
@@ -116,13 +114,13 @@ public class BatteryStatsHistoryTest {
assertEquals(0, history.getHistoryUsedSize());
// create a new BatteryStatsHistory object, it will pick up existing history files.
- BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- null, mClock);
- // verify constructor can pick up all files from file system.
+ BatteryStatsHistory history2 =
+ new BatteryStatsHistory(mHistoryBuffer, mSystemDir, () -> 32);
+ // verify construct can pick up all files from file system.
verifyFileNumbers(history2, fileList);
verifyActiveFile(history2, "33.bin");
- history2.reset();
+ history2.resetAllFiles();
createActiveFile(history2);
// verify all existing files are deleted.
for (int i = 2; i < 33; ++i) {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 570b2ee617f5..713e78638b95 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -63,7 +63,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
MockBatteryStatsImpl(Clock clock, File historyDirectory) {
super(clock, historyDirectory);
initTimersAndCounters();
- setMaxHistoryBuffer(128 * 1024);
setExternalStatsSyncLocked(mExternalStatsSync);
informThatAllExternalStatsAreFlushed();
@@ -105,6 +104,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
return mForceOnBattery ? true : super.isOnBattery();
}
+ public void forceRecordAllHistory() {
+ mHaveBatteryLevel = true;
+ mRecordingHistory = true;
+ mRecordAllHistory = true;
+ }
+
public TimeBase getOnBatteryBackgroundTimeBase(int uid) {
return getUidStatsLocked(uid).mOnBatteryBackgroundTimeBase;
}
@@ -196,14 +201,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
@GuardedBy("this")
public MockBatteryStatsImpl setMaxHistoryFiles(int maxHistoryFiles) {
mConstants.MAX_HISTORY_FILES = maxHistoryFiles;
- mConstants.onChange();
return this;
}
@GuardedBy("this")
public MockBatteryStatsImpl setMaxHistoryBuffer(int maxHistoryBuffer) {
mConstants.MAX_HISTORY_BUFFER = maxHistoryBuffer;
- mConstants.onChange();
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d62ac99e530b..598a22bbde39 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -93,7 +93,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.os.Parcel;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -2448,35 +2447,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testGetNotificationChannelGroup() throws Exception {
- NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
- NotificationChannel base =
- new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
- base.setGroup("not");
- NotificationChannel convo =
- new NotificationChannel("convo", "belongs to notDeleted", IMPORTANCE_DEFAULT);
- convo.setGroup("not");
- convo.setConversationId("not deleted", "banana");
-
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, base, true, false);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, convo, true, false);
- mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
-
- NotificationChannelGroup g
- = mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1);
- Parcel parcel = Parcel.obtain();
- g.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
-
- NotificationChannelGroup g2
- = mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1);
- Parcel parcel2 = Parcel.obtain();
- g2.writeToParcel(parcel2, 0);
- parcel2.setDataPosition(0);
- }
-
- @Test
public void testOnUserRemoved() throws Exception {
int[] user0Uids = {98, 235, 16, 3782};
int[] user1Uids = new int[user0Uids.length];
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index c12f0a965146..d72cfc70fc02 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -17,7 +17,9 @@ package com.android.server.notification;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
@@ -30,6 +32,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.Person;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -39,8 +42,11 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.ContactsContract;
+import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableString;
+import android.util.ArraySet;
+import android.util.LruCache;
import androidx.test.runner.AndroidJUnit4;
@@ -323,6 +329,69 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase {
isNull()); // sort order
}
+ @Test
+ public void testValidatePeople_needsLookupWhenNoCache() {
+ final Context mockContext = mock(Context.class);
+ final ContentResolver mockContentResolver = mock(ContentResolver.class);
+ when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+ final NotificationUsageStats mockNotificationUsageStats =
+ mock(NotificationUsageStats.class);
+
+ // Create validator with empty cache
+ ValidateNotificationPeople vnp = new ValidateNotificationPeople();
+ LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5);
+ vnp.initForTests(mockContext, mockNotificationUsageStats, cache);
+
+ NotificationRecord record = getNotificationRecord();
+ String[] callNumber = new String[]{"tel:12345678910"};
+ setNotificationPeople(record, callNumber);
+
+ // Returned ranking reconsideration not null indicates that there is a lookup to be done
+ RankingReconsideration rr = vnp.validatePeople(mockContext, record);
+ assertNotNull(rr);
+ }
+
+ @Test
+ public void testValidatePeople_noLookupWhenCached_andPopulatesContactInfo() {
+ final Context mockContext = mock(Context.class);
+ final ContentResolver mockContentResolver = mock(ContentResolver.class);
+ when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+ when(mockContext.getUserId()).thenReturn(1);
+ final NotificationUsageStats mockNotificationUsageStats =
+ mock(NotificationUsageStats.class);
+
+ // Information to be passed in & returned from the lookup result
+ String lookup = "lookup:contactinfohere";
+ String lookupTel = "16175551234";
+ float affinity = 0.7f;
+
+ // Create a fake LookupResult for the data we'll pass in
+ LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5);
+ ValidateNotificationPeople.LookupResult lr =
+ mock(ValidateNotificationPeople.LookupResult.class);
+ when(lr.getAffinity()).thenReturn(affinity);
+ when(lr.getPhoneNumbers()).thenReturn(new ArraySet<>(new String[]{lookupTel}));
+ when(lr.isExpired()).thenReturn(false);
+ cache.put(ValidateNotificationPeople.getCacheKey(1, lookup), lr);
+
+ // Create validator with the established cache
+ ValidateNotificationPeople vnp = new ValidateNotificationPeople();
+ vnp.initForTests(mockContext, mockNotificationUsageStats, cache);
+
+ NotificationRecord record = getNotificationRecord();
+ String[] peopleInfo = new String[]{lookup};
+ setNotificationPeople(record, peopleInfo);
+
+ // Returned ranking reconsideration null indicates that there is no pending work to be done
+ RankingReconsideration rr = vnp.validatePeople(mockContext, record);
+ assertNull(rr);
+
+ // Confirm that the affinity & phone number made it into our record
+ assertEquals(affinity, record.getContactAffinity(), 1e-8);
+ assertNotNull(record.getPhoneNumbers());
+ assertTrue(record.getPhoneNumbers().contains(lookupTel));
+ }
+
// Creates a cursor that points to one item of Contacts data with the specified
// columns.
private Cursor makeMockCursor(int id, String lookupKey, int starred, int hasPhone) {
@@ -365,4 +434,17 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase {
String resultString = Arrays.toString(result);
assertEquals(message + ": arrays differ", expectedString, resultString);
}
+
+ private NotificationRecord getNotificationRecord() {
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ Notification notification = mock(Notification.class);
+ when(sbn.getNotification()).thenReturn(notification);
+ return new NotificationRecord(mContext, sbn, mock(NotificationChannel.class));
+ }
+
+ private void setNotificationPeople(NotificationRecord r, String[] people) {
+ Bundle extras = new Bundle();
+ extras.putObject(Notification.EXTRA_PEOPLE_LIST, people);
+ r.getSbn().getNotification().extras = extras;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 75ecfd870eb2..d5e336b1cf2f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -228,7 +228,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
mAtm.getTaskChangeNotificationController();
spyOn(taskChangeNotifier);
- mAtm.setResumedActivityUncheckLocked(fullScreenActivityA, "resumeA");
+ mAtm.setLastResumedActivityUncheckLocked(fullScreenActivityA, "resumeA");
verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
eq(true) /* focused */);
reset(taskChangeNotifier);
@@ -237,7 +237,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
.build();
final Task taskB = fullScreenActivityB.getTask();
- mAtm.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
+ mAtm.setLastResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
eq(false) /* focused */);
verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
@@ -295,6 +295,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
activity1.moveFocusableActivityToTop("test");
assertEquals(activity1.getUid(), pendingTopUid[0]);
verify(mAtm).updateOomAdj();
+ verify(mAtm).setLastResumedActivityUncheckLocked(any(), eq("test"));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 88e58eab58aa..8546763aebec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -281,7 +281,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
// Simulate the app transition finishing
- mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0);
+ mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0);
verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index da72030b313d..9274eb3f1490 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -46,7 +47,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -65,6 +65,7 @@ import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizerToken;
+import android.window.TaskFragmentTransaction;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
@@ -90,6 +91,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private TaskFragmentOrganizerController mController;
private WindowOrganizerController mWindowOrganizerController;
+ private TransitionController mTransitionController;
private TaskFragmentOrganizer mOrganizer;
private TaskFragmentOrganizerToken mOrganizerToken;
private ITaskFragmentOrganizer mIOrganizer;
@@ -107,9 +109,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private Task mTask;
@Before
- public void setup() {
+ public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mWindowOrganizerController = mAtm.mWindowOrganizerController;
+ mTransitionController = mWindowOrganizerController.mTransitionController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken();
@@ -128,11 +131,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
spyOn(mController);
spyOn(mOrganizer);
spyOn(mTaskFragment);
+ spyOn(mWindowOrganizerController);
+ spyOn(mTransitionController);
doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+
+ // To prevent it from calling the real server.
+ doNothing().when(mOrganizer).onTransactionHandled(any(), any());
}
@Test
@@ -866,7 +874,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertFalse(parentTask.shouldBeVisible(null));
// Verify the info changed callback still occurred despite the task being invisible
- reset(mOrganizer);
+ clearInvocations(mOrganizer);
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentInfoChanged(any(), any());
@@ -899,7 +907,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer).onTaskFragmentInfoChanged(any(), any());
// Verify the info changed callback is not called when the task is invisible
- reset(mOrganizer);
+ clearInvocations(mOrganizer);
doReturn(false).when(task).shouldBeVisible(any());
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents();
@@ -1092,6 +1100,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.that(mTaskFragment.getBounds()).isEqualTo(task.getBounds());
}
+ @Test
+ public void testOnTransactionReady_invokeOnTransactionHandled() {
+ mController.registerOrganizer(mIOrganizer);
+ final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+ mOrganizer.onTransactionReady(transaction);
+
+ // Organizer should always trigger #onTransactionHandled when receives #onTransactionReady
+ verify(mOrganizer).onTransactionHandled(eq(transaction.getTransactionToken()), any());
+ verify(mOrganizer, never()).applyTransaction(any());
+ }
+
+ @Test
+ public void testDispatchTransaction_deferTransitionReady() {
+ mController.registerOrganizer(mIOrganizer);
+ setupMockParent(mTaskFragment, mTask);
+ final ArgumentCaptor<IBinder> tokenCaptor = ArgumentCaptor.forClass(IBinder.class);
+ final ArgumentCaptor<WindowContainerTransaction> wctCaptor =
+ ArgumentCaptor.forClass(WindowContainerTransaction.class);
+ doReturn(true).when(mTransitionController).isCollecting();
+
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ // Defer transition when send TaskFragment transaction during transition collection.
+ verify(mTransitionController).deferTransitionReady();
+ verify(mOrganizer).onTransactionHandled(tokenCaptor.capture(), wctCaptor.capture());
+
+ mController.onTransactionHandled(mIOrganizer, tokenCaptor.getValue(), wctCaptor.getValue());
+
+ // Apply the organizer change and continue transition.
+ verify(mWindowOrganizerController).applyTransaction(wctCaptor.getValue());
+ verify(mTransitionController).continueTransitionReady();
+ }
+
/**
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction} to apply the transaction,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 13da1543cfb8..d2cb7ba5d311 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -310,11 +310,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void startKeyguardExitAnimation(long startTime) {
+ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
}
@Override
- public int applyKeyguardOcclusionChange(boolean notify) {
+ public int applyKeyguardOcclusionChange(boolean keyguardOccludingStarted) {
return 0;
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index be7fb7315955..8df3548465bd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -21,7 +21,6 @@ import android.platform.test.annotations.Presubmit
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.common.ComponentMatcher
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 472a0fa376bf..eddb55346af8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.IComponentMatcher
/**
@@ -28,7 +28,7 @@ import com.android.server.wm.traces.common.IComponentMatcher
*/
fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
assertWm {
- this.isAboveAppWindowVisible(ComponentMatcher.STATUS_BAR)
+ this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR)
}
}
@@ -38,7 +38,7 @@ fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
*/
fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
assertWm {
- this.isAboveAppWindowVisible(ComponentMatcher.NAV_BAR)
+ this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR)
}
}
@@ -48,10 +48,10 @@ fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
*/
fun FlickerTestParameter.navBarWindowIsVisibleAtStartAndEnd() {
assertWmStart {
- this.isAboveAppWindowVisible(ComponentMatcher.NAV_BAR)
+ this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR)
}
assertWmEnd {
- this.isAboveAppWindowVisible(ComponentMatcher.NAV_BAR)
+ this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR)
}
}
@@ -61,7 +61,7 @@ fun FlickerTestParameter.navBarWindowIsVisibleAtStartAndEnd() {
*/
fun FlickerTestParameter.taskBarWindowIsAlwaysVisible() {
assertWm {
- this.isAboveAppWindowVisible(ComponentMatcher.TASK_BAR)
+ this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR)
}
}
@@ -71,7 +71,7 @@ fun FlickerTestParameter.taskBarWindowIsAlwaysVisible() {
*/
fun FlickerTestParameter.taskBarWindowIsVisibleAtEnd() {
assertWmEnd {
- this.isAboveAppWindowVisible(ComponentMatcher.TASK_BAR)
+ this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR)
}
}
@@ -114,10 +114,10 @@ fun FlickerTestParameter.entireScreenCovered(allStates: Boolean = true) {
*/
fun FlickerTestParameter.navBarLayerIsVisibleAtStartAndEnd() {
assertLayersStart {
- this.isVisible(ComponentMatcher.NAV_BAR)
+ this.isVisible(ComponentNameMatcher.NAV_BAR)
}
assertLayersEnd {
- this.isVisible(ComponentMatcher.NAV_BAR)
+ this.isVisible(ComponentNameMatcher.NAV_BAR)
}
}
@@ -136,7 +136,7 @@ fun FlickerTestParameter.taskBarLayerIsVisibleAtStartAndEnd() {
*/
fun FlickerTestParameter.taskBarLayerIsVisibleAtStart() {
assertLayersStart {
- this.isVisible(ComponentMatcher.TASK_BAR)
+ this.isVisible(ComponentNameMatcher.TASK_BAR)
}
}
@@ -146,7 +146,7 @@ fun FlickerTestParameter.taskBarLayerIsVisibleAtStart() {
*/
fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() {
assertLayersEnd {
- this.isVisible(ComponentMatcher.TASK_BAR)
+ this.isVisible(ComponentNameMatcher.TASK_BAR)
}
}
@@ -156,10 +156,10 @@ fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() {
*/
fun FlickerTestParameter.statusBarLayerIsVisibleAtStartAndEnd() {
assertLayersStart {
- this.isVisible(ComponentMatcher.STATUS_BAR)
+ this.isVisible(ComponentNameMatcher.STATUS_BAR)
}
assertLayersEnd {
- this.isVisible(ComponentMatcher.STATUS_BAR)
+ this.isVisible(ComponentNameMatcher.STATUS_BAR)
}
}
@@ -171,7 +171,7 @@ fun FlickerTestParameter.navBarLayerPositionAtStart() {
assertLayersStart {
val display = this.entry.displays.firstOrNull { !it.isVirtual }
?: error("There is no display!")
- this.visibleRegion(ComponentMatcher.NAV_BAR)
+ this.visibleRegion(ComponentNameMatcher.NAV_BAR)
.coversExactly(WindowUtils.getNavigationBarPosition(display, isGesturalNavigation))
}
}
@@ -184,7 +184,7 @@ fun FlickerTestParameter.navBarLayerPositionAtEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
- this.visibleRegion(ComponentMatcher.NAV_BAR)
+ this.visibleRegion(ComponentNameMatcher.NAV_BAR)
.coversExactly(WindowUtils.getNavigationBarPosition(display, isGesturalNavigation))
}
}
@@ -206,7 +206,7 @@ fun FlickerTestParameter.statusBarLayerPositionAtStart() {
assertLayersStart {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
- this.visibleRegion(ComponentMatcher.STATUS_BAR)
+ this.visibleRegion(ComponentNameMatcher.STATUS_BAR)
.coversExactly(WindowUtils.getStatusBarPosition(display))
}
}
@@ -219,7 +219,7 @@ fun FlickerTestParameter.statusBarLayerPositionAtEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
- this.visibleRegion(ComponentMatcher.STATUS_BAR)
+ this.visibleRegion(ComponentNameMatcher.STATUS_BAR)
.coversExactly(WindowUtils.getStatusBarPosition(display))
}
}
@@ -244,7 +244,7 @@ fun FlickerTestParameter.snapshotStartingWindowLayerCoversExactlyOnApp(
invoke("snapshotStartingWindowLayerCoversExactlyOnApp") {
val snapshotLayers = it.subjects.filter { subject ->
subject.name.contains(
- ComponentMatcher.SNAPSHOT.toLayerName()) && subject.isVisible
+ ComponentNameMatcher.SNAPSHOT.toLayerName()) && subject.isVisible
}
// Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
if (snapshotLayers.isNotEmpty()) {
@@ -291,10 +291,10 @@ fun FlickerTestParameter.replacesLayer(
val assertion = this.isVisible(originalLayer)
if (ignoreEntriesWithRotationLayer) {
- assertion.then().isVisible(ComponentMatcher.ROTATION, isOptional = true)
+ assertion.then().isVisible(ComponentNameMatcher.ROTATION, isOptional = true)
}
if (ignoreSnapshot) {
- assertion.then().isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ assertion.then().isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
}
if (ignoreSplashscreen) {
assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 7ff093474399..cb197cdf04d3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -24,7 +24,7 @@ import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.traces.common.ComponentMatcher.Companion.LAUNCHER
+import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.LAUNCHER
import org.junit.Test
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index af3a8c502008..b8fe9f9df882 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -26,7 +26,7 @@ import androidx.window.extensions.WindowExtensions
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.embedding.ActivityEmbeddingComponent
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.windowmanager.WindowManagerState.Companion.STATE_RESUMED
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -35,7 +35,7 @@ import org.junit.Assume.assumeNotNull
class ActivityEmbeddingAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.ACTIVITY_EMBEDDING_LAUNCHER_NAME,
- component: IComponentMatcher = MAIN_ACTIVITY_COMPONENT,
+ component: ComponentNameMatcher = MAIN_ACTIVITY_COMPONENT,
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
index bd0ae4d47383..34f9ce43b4bc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
@@ -21,12 +21,11 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.provider.MediaStore
-import com.android.server.wm.traces.common.ComponentMatcher
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
class CameraAppHelper @JvmOverloads constructor(
- instrumentation: Instrumentation,
- private val pkgManager: PackageManager = instrumentation.context.packageManager
+ instrumentation: Instrumentation,
+ pkgManager: PackageManager = instrumentation.context.packageManager
) : StandardAppHelper(instrumentation, getCameraLauncherName(pkgManager),
getCameraComponent(pkgManager)){
companion object{
@@ -40,9 +39,9 @@ class CameraAppHelper @JvmOverloads constructor(
?: error("unable to resolve camera activity")
}
- private fun getCameraComponent(pkgManager: PackageManager): IComponentMatcher {
+ private fun getCameraComponent(pkgManager: PackageManager): ComponentNameMatcher {
val resolveInfo = getResolveInfo(pkgManager)
- return ComponentMatcher(resolveInfo.activityInfo.packageName,
+ return ComponentNameMatcher(resolveInfo.activityInfo.packageName,
className = resolveInfo.activityInfo.name)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
index 28858d4843a1..b696fc30c19f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -20,13 +20,13 @@ import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class FixedOrientationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index f536a151d8fd..e01cceb5d636 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -23,7 +23,7 @@ import android.view.WindowInsets.Type.statusBars
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import java.util.regex.Pattern
@@ -33,7 +33,7 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
private val rotation: Int,
private val imePackageName: String = IME_PACKAGE,
launcherName: String = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(wmHelper: WindowManagerStateHelper) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index db64c4769715..b672b1bc0da5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -22,14 +22,14 @@ import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
open class ImeAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.IME_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.IME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
index f74054ea4459..df47e9dfacd9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -20,16 +20,14 @@ import android.app.Instrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class ImeEditorPopupDialogAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- private val rotation: Int,
- private val imePackageName: String = IME_PACKAGE,
launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(wmHelper: WindowManagerStateHelper) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
index 7f8b5633391b..d3945c1749e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -20,13 +20,13 @@ import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class ImeStateInitializeHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.IME_ACTIVITY_INITIALIZE_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
index 149576e4ec1d..9fb574cf1c04 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -23,14 +23,14 @@ import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class NewTasksAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index a9769d01caff..a1dbeeaef5cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -20,13 +20,13 @@ import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class NonResizeableAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
index 50d036c4450f..b031a4523ab0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -22,14 +22,14 @@ import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class NotificationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.NOTIFICATION_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.NOTIFICATION_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 459ca5b3208f..6d466d72d7dd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -20,13 +20,13 @@ import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class SeamlessRotationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SEAMLESS_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
index 4952dbaeb551..804ab3864591 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
@@ -20,13 +20,13 @@ import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class ShowWhenLockedAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SHOW_WHEN_LOCKED_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.SHOW_WHEN_LOCKED_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index 6bddcac21a4d..5da273a7f1dc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -20,13 +20,13 @@ import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class SimpleAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SIMPLE_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index a17344f3879b..060e9af4a51c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -23,14 +23,14 @@ import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class TwoActivitiesAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
- component: IComponentMatcher =
+ component: ComponentNameMatcher =
ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 6b8fde21e53d..f6f3f58f29cb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -83,7 +83,7 @@ class CloseImeAutoOpenWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
}
}
@@ -91,7 +91,7 @@ class CloseImeAutoOpenWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(ComponentMatcher.IME)
+ this.isInvisible(ComponentNameMatcher.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index a92ecb953a01..52f561ec4497 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -92,7 +92,7 @@ class CloseImeAutoOpenWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTes
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
}
}
@@ -100,7 +100,7 @@ class CloseImeAutoOpenWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTes
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(ComponentMatcher.IME)
+ this.isInvisible(ComponentNameMatcher.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
index 0e4d1dd009e2..c6e25d3de4cd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -28,7 +28,7 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,7 +41,7 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
- private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation, testSpec.startRotation)
+ private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
@@ -102,12 +102,12 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t
@Test
fun imeLayerAndImeSnapshotVisibleOnScreen() {
testSpec.assertLayers {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
.then()
- .isVisible(ComponentMatcher.IME_SNAPSHOT)
+ .isVisible(ComponentNameMatcher.IME_SNAPSHOT)
.then()
- .isInvisible(ComponentMatcher.IME_SNAPSHOT, isOptional = true)
- .isInvisible(ComponentMatcher.IME)
+ .isInvisible(ComponentNameMatcher.IME_SNAPSHOT, isOptional = true)
+ .isInvisible(ComponentNameMatcher.IME)
}
}
@@ -118,7 +118,7 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t
this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") {
val imeSnapshotLayers = it.subjects.filter { subject ->
subject.name.contains(
- ComponentMatcher.IME_SNAPSHOT.toLayerName()
+ ComponentNameMatcher.IME_SNAPSHOT.toLayerName()
) && subject.isVisible
}
if (imeSnapshotLayers.isNotEmpty()) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 452aa63e2bf9..23bd2200397a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -73,9 +73,9 @@ class CloseImeWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpe
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- ComponentMatcher.IME,
- ComponentMatcher.SPLASH_SCREEN,
- ComponentMatcher.SNAPSHOT))
+ ComponentNameMatcher.IME,
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 856df260c2be..8ce184072d32 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,9 +78,9 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
listOf(
- ComponentMatcher.IME,
- ComponentMatcher.SPLASH_SCREEN,
- ComponentMatcher.SNAPSHOT
+ ComponentNameMatcher.IME,
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT
)
)
}
@@ -93,8 +93,8 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
listOf(
- ComponentMatcher.IME,
- ComponentMatcher.SPLASH_SCREEN
+ ComponentNameMatcher.IME,
+ ComponentNameMatcher.SPLASH_SCREEN
)
)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 19cab3c20dae..9c99d966e345 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -18,52 +18,52 @@
package com.android.server.wm.flicker.ime
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
fun FlickerTestParameter.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(ComponentMatcher.IME)
+ this.isInvisible(ComponentNameMatcher.IME)
.then()
- .isVisible(ComponentMatcher.IME)
+ .isVisible(ComponentNameMatcher.IME)
}
}
fun FlickerTestParameter.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
.then()
- .isInvisible(ComponentMatcher.IME)
+ .isInvisible(ComponentNameMatcher.IME)
}
}
fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.isNonAppWindowVisible(ComponentMatcher.IME)
+ this.isNonAppWindowVisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowInvisible(ComponentMatcher.IME)
+ .isNonAppWindowInvisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowVisible(ComponentMatcher.IME)
+ .isNonAppWindowVisible(ComponentNameMatcher.IME)
}
} else {
assertWm {
- this.isNonAppWindowVisible(ComponentMatcher.IME)
+ this.isNonAppWindowVisible(ComponentNameMatcher.IME)
}
}
}
fun FlickerTestParameter.imeWindowBecomesVisible() {
assertWm {
- this.isNonAppWindowInvisible(ComponentMatcher.IME)
+ this.isNonAppWindowInvisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowVisible(ComponentMatcher.IME)
+ .isNonAppWindowVisible(ComponentNameMatcher.IME)
}
}
fun FlickerTestParameter.imeWindowBecomesInvisible() {
assertWm {
- this.isNonAppWindowVisible(ComponentMatcher.IME)
+ this.isNonAppWindowVisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowInvisible(ComponentMatcher.IME)
+ .isNonAppWindowInvisible(ComponentNameMatcher.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index 4569a5b4910e..a04a50f02c26 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -30,7 +30,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
@@ -101,7 +101,7 @@ class LaunchAppShowImeAndDialogThemeAppTest(
@Test
fun imeLayerExistsEnd() {
testSpec.assertLayersEnd {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
}
}
@@ -112,7 +112,7 @@ class LaunchAppShowImeAndDialogThemeAppTest(
@Test
fun imeSnapshotNotVisible() {
testSpec.assertLayers {
- this.isInvisible(ComponentMatcher.IME_SNAPSHOT)
+ this.isInvisible(ComponentNameMatcher.IME_SNAPSHOT)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
index 977719c5f670..04e4bc94de9c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -28,7 +28,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -114,7 +114,7 @@ class LaunchAppShowImeOnStartTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun imeLayerNotExistsStart() {
testSpec.assertLayersStart {
- this.isInvisible(ComponentMatcher.IME)
+ this.isInvisible(ComponentNameMatcher.IME)
}
}
@@ -125,7 +125,7 @@ class LaunchAppShowImeOnStartTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun imeLayerExistsEnd() {
testSpec.assertLayersEnd {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
index 16cf22abecc2..9475734ef3f5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
@@ -31,7 +31,7 @@ import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
@@ -138,10 +138,10 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
Assume.assumeTrue(testSpec.isGesturalNavigation)
Assume.assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayersStart {
- this.isVisible(ComponentMatcher.NAV_BAR)
+ this.isVisible(ComponentNameMatcher.NAV_BAR)
}
testSpec.assertLayersEnd {
- this.isInvisible(ComponentMatcher.NAV_BAR)
+ this.isInvisible(ComponentNameMatcher.NAV_BAR)
}
}
@@ -156,10 +156,10 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
Assume.assumeTrue(testSpec.isGesturalNavigation)
Assume.assumeFalse(testSpec.isTablet)
testSpec.assertLayersStart {
- this.isVisible(ComponentMatcher.STATUS_BAR)
+ this.isVisible(ComponentNameMatcher.STATUS_BAR)
}
testSpec.assertLayersEnd {
- this.isInvisible(ComponentMatcher.STATUS_BAR)
+ this.isInvisible(ComponentNameMatcher.STATUS_BAR)
}
}
@@ -215,10 +215,10 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
Assume.assumeFalse(testSpec.isTablet)
Assume.assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayersStart {
- this.isVisible(ComponentMatcher.STATUS_BAR)
+ this.isVisible(ComponentNameMatcher.STATUS_BAR)
}
testSpec.assertLayersEnd {
- this.isInvisible(ComponentMatcher.STATUS_BAR)
+ this.isInvisible(ComponentNameMatcher.STATUS_BAR)
}
}
@@ -235,7 +235,7 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun imeLayerIsVisibleAndAssociatedWithAppWidow() {
testSpec.assertLayersStart {
- isVisible(ComponentMatcher.IME).visibleRegion(ComponentMatcher.IME)
+ isVisible(ComponentNameMatcher.IME).visibleRegion(ComponentNameMatcher.IME)
.coversAtMost(
isVisible(imeTestApp)
.visibleRegion(imeTestApp).region
@@ -243,10 +243,10 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes
}
testSpec.assertLayers {
this.invoke("imeLayerIsVisibleAndAlignAppWidow") {
- val imeVisibleRegion = it.visibleRegion(ComponentMatcher.IME)
+ val imeVisibleRegion = it.visibleRegion(ComponentNameMatcher.IME)
val appVisibleRegion = it.visibleRegion(imeTestApp)
if (imeVisibleRegion.region.isNotEmpty) {
- it.isVisible(ComponentMatcher.IME)
+ it.isVisible(ComponentNameMatcher.IME)
imeVisibleRegion.coversAtMost(appVisibleRegion.region)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 2207fe571e9f..2e22e6224813 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -29,7 +29,7 @@ import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -84,11 +84,11 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
// depends on how much of the animation transactions are sent to SF at once
// sometimes this layer appears for 2-3 frames, sometimes for only 1
- val recentTaskComponent = ComponentMatcher("", "RecentTaskScreenshotSurface")
+ val recentTaskComponent = ComponentNameMatcher("", "RecentTaskScreenshotSurface")
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(ComponentMatcher.SPLASH_SCREEN,
- ComponentMatcher.SNAPSHOT, recentTaskComponent)
+ listOf(ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT, recentTaskComponent)
)
}
}
@@ -97,11 +97,11 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Presubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- val component = ComponentMatcher("", "RecentTaskScreenshotSurface")
+ val component = ComponentNameMatcher("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows = listOf(ComponentMatcher.SPLASH_SCREEN,
- ComponentMatcher.SNAPSHOT,
+ ignoreWindows = listOf(ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
component)
)
}
@@ -111,9 +111,9 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Test
fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(ComponentMatcher.LAUNCHER)
+ this.isAppWindowVisible(ComponentNameMatcher.LAUNCHER)
.then()
- .isAppWindowInvisible(ComponentMatcher.LAUNCHER)
+ .isAppWindowInvisible(ComponentNameMatcher.LAUNCHER)
}
}
@@ -157,11 +157,11 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
fun imeLayerIsBecomesVisibleLegacy() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
.then()
- .isInvisible(ComponentMatcher.IME)
+ .isInvisible(ComponentNameMatcher.IME)
.then()
- .isVisible(ComponentMatcher.IME)
+ .isVisible(ComponentNameMatcher.IME)
}
}
@@ -170,7 +170,7 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
fun imeLayerBecomesVisibleShellTransit() {
Assume.assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(ComponentMatcher.IME)
+ this.isVisible(ComponentNameMatcher.IME)
}
}
@@ -178,9 +178,9 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Test
fun appLayerReplacesLauncher() {
testSpec.assertLayers {
- this.isVisible(ComponentMatcher.LAUNCHER)
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index be7b80e8db82..4f47ec439da8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -31,7 +31,7 @@ import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -177,20 +177,20 @@ open class SwitchImeWindowsFromGestureNavTest(
@Test
open fun imeLayerIsVisibleWhenSwitchingToImeApp() {
testSpec.assertLayersStart {
- isVisible(ComponentMatcher.IME)
+ isVisible(ComponentNameMatcher.IME)
}
testSpec.assertLayersTag(TAG_IME_VISIBLE) {
- isVisible(ComponentMatcher.IME)
+ isVisible(ComponentNameMatcher.IME)
}
testSpec.assertLayersEnd {
- isVisible(ComponentMatcher.IME)
+ isVisible(ComponentNameMatcher.IME)
}
}
@Test
fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
- isInvisible(ComponentMatcher.IME)
+ isInvisible(ComponentNameMatcher.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt
index a8c0a0b55009..5ac1712adbf2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt
@@ -24,7 +24,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarWindowIsVisibleAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 86b8e5fa1647..33c280ea78bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.FixMethodOrder
import org.junit.Test
@@ -118,7 +118,7 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Test
fun launcherWindowNotOnTop() {
testSpec.assertWm {
- this.isAppWindowNotOnTop(ComponentMatcher.LAUNCHER)
+ this.isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER)
}
}
@@ -128,7 +128,7 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Presubmit
@Test
fun launcherLayerNotVisible() {
- testSpec.assertLayers { this.isInvisible(ComponentMatcher.LAUNCHER) }
+ testSpec.assertLayers { this.isInvisible(ComponentNameMatcher.LAUNCHER) }
}
companion object {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
index 2d4d7986a67e..bece406d5a96 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
@@ -19,7 +19,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Test
/**
@@ -46,7 +46,7 @@ abstract class OpenAppFromLauncherTransition(
*/
open fun appLayerReplacesLauncher() {
testSpec.replacesLayer(
- ComponentMatcher.LAUNCHER, testApp,
+ ComponentNameMatcher.LAUNCHER, testApp,
ignoreEntriesWithRotationLayer = true, ignoreSnapshot = true,
ignoreSplashscreen = true
)
@@ -61,12 +61,12 @@ abstract class OpenAppFromLauncherTransition(
@Test
open fun appWindowReplacesLauncherAsTopWindow() {
testSpec.assertWm {
- this.isAppWindowOnTop(ComponentMatcher.LAUNCHER)
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
.then()
.isAppWindowOnTop(
testApp
- .or(ComponentMatcher.SNAPSHOT)
- .or(ComponentMatcher.SPLASH_SCREEN)
+ .or(ComponentNameMatcher.SNAPSHOT)
+ .or(ComponentNameMatcher.SPLASH_SCREEN)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index 220e4caf5faa..bfc7b39f9d9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -26,7 +26,6 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index 9ed1bde86647..e517c2ad2c49 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -86,9 +86,9 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) :
testSpec.assertWm {
this.hasNoVisibleAppWindow()
.then()
- .isAppWindowOnTop(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowOnTop(ComponentMatcher.SPLASH_SCREEN, isOptional = true)
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
.then()
.isAppWindowOnTop(testApp)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index 29730591c957..75311eaf5c66 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -26,7 +26,7 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -88,7 +88,7 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet
testSpec.assertWm {
this.hasNoVisibleAppWindow()
.then()
- .isAppWindowOnTop(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isAppWindowOnTop(showWhenLockedApp)
}
@@ -100,7 +100,7 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet
testSpec.assertLayers {
this.isInvisible(showWhenLockedApp)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isVisible(showWhenLockedApp)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
index 1d8b0a64c70f..ecc60b8d45c0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
@@ -22,7 +22,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.Ignore
import org.junit.Test
@@ -77,9 +77,9 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) :
testSpec.assertWm {
this.hasNoVisibleAppWindow()
.then()
- .isAppWindowOnTop(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowOnTop(ComponentMatcher.SPLASH_SCREEN, isOptional = true)
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
.then()
.isAppWindowOnTop(testApp)
}
@@ -152,7 +152,7 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) :
@Test
fun statusBarLayerIsVisibleAtEnd() {
testSpec.assertLayersEnd {
- this.isVisible(ComponentMatcher.STATUS_BAR)
+ this.isVisible(ComponentNameMatcher.STATUS_BAR)
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 866e819bf5fc..78baddff582c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -34,7 +34,6 @@ import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
-import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 647607794fa9..53be7d43cce8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -29,7 +29,7 @@ import com.android.server.wm.flicker.annotation.FlickerServiceCompatible
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -76,9 +76,9 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) :
fun navBarLayerVisibilityChanges() {
Assume.assumeFalse(testSpec.isTablet)
testSpec.assertLayers {
- this.isInvisible(ComponentMatcher.NAV_BAR)
+ this.isInvisible(ComponentNameMatcher.NAV_BAR)
.then()
- .isVisible(ComponentMatcher.NAV_BAR)
+ .isVisible(ComponentNameMatcher.NAV_BAR)
}
}
@@ -102,9 +102,9 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) :
fun navBarWindowsVisibilityChanges() {
Assume.assumeFalse(testSpec.isTablet)
testSpec.assertWm {
- this.isNonAppWindowInvisible(ComponentMatcher.NAV_BAR)
+ this.isNonAppWindowInvisible(ComponentNameMatcher.NAV_BAR)
.then()
- .isAboveAppWindowVisible(ComponentMatcher.NAV_BAR)
+ .isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR)
}
}
@@ -117,7 +117,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) :
fun taskBarLayerIsVisibleAtEnd() {
Assume.assumeTrue(testSpec.isTablet)
testSpec.assertLayersEnd {
- this.isVisible(ComponentMatcher.TASK_BAR)
+ this.isVisible(ComponentNameMatcher.TASK_BAR)
}
}
@@ -130,7 +130,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) :
@Test
override fun statusBarLayerIsVisibleAtStartAndEnd() {
testSpec.assertLayersEnd {
- this.isVisible(ComponentMatcher.STATUS_BAR)
+ this.isVisible(ComponentNameMatcher.STATUS_BAR)
}
}
@@ -180,7 +180,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) :
fun navBarLayerIsVisibleAtEnd() {
Assume.assumeFalse(testSpec.isTablet)
testSpec.assertLayersEnd {
- this.isVisible(ComponentMatcher.NAV_BAR)
+ this.isVisible(ComponentNameMatcher.NAV_BAR)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 5d2b56707de9..8658c03acf60 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -24,7 +24,7 @@ import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Test
/**
@@ -65,9 +65,9 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test
.then()
.isInvisible(testApp, isOptional = true)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
- .isVisible(ComponentMatcher.SPLASH_SCREEN, isOptional = true)
+ .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
.then()
.isVisible(testApp)
}
@@ -77,9 +77,9 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test
testSpec.assertLayers {
this.isInvisible(testApp)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
- .isVisible(ComponentMatcher.SPLASH_SCREEN, isOptional = true)
+ .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
.then()
.isVisible(testApp)
}
@@ -110,9 +110,9 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test
testSpec.assertWm {
this.isAppWindowInvisible(testApp)
.then()
- .isAppWindowVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowVisible(ComponentMatcher.SPLASH_SCREEN, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
.then()
.isAppWindowVisible(testApp)
}
@@ -130,8 +130,8 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test
.then()
.isAppWindowOnTop(
testApp
- .or(ComponentMatcher.SNAPSHOT)
- .or(ComponentMatcher.SPLASH_SCREEN)
+ .or(ComponentNameMatcher.SNAPSHOT)
+ .or(ComponentNameMatcher.SPLASH_SCREEN)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index b482e5f3c3fb..fe5e74b87f93 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -30,9 +30,9 @@ import com.android.server.wm.flicker.helpers.NewTasksAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.testapp.ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME
import com.android.server.wm.flicker.testapp.ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
-import com.android.server.wm.traces.common.ComponentMatcher
-import com.android.server.wm.traces.common.ComponentMatcher.Companion.SPLASH_SCREEN
-import com.android.server.wm.traces.common.ComponentMatcher.Companion.WALLPAPER_BBQ_WRAPPER
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.SPLASH_SCREEN
+import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
import com.android.server.wm.traces.common.IComponentMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.FixMethodOrder
@@ -117,7 +117,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
@Test
fun launcherWindowIsNeverVisible() {
testSpec.assertWm {
- this.isAppWindowInvisible(ComponentMatcher.LAUNCHER)
+ this.isAppWindowInvisible(ComponentNameMatcher.LAUNCHER)
}
}
@@ -130,7 +130,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
@Test
fun launcherLayerIsNeverVisible() {
testSpec.assertLayers {
- this.isInvisible(ComponentMatcher.LAUNCHER)
+ this.isInvisible(ComponentNameMatcher.LAUNCHER)
}
}
@@ -140,7 +140,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
@Postsubmit
@Test
fun colorLayerIsVisibleDuringTransition() {
- val bgColorLayer = ComponentMatcher("", "colorBackgroundLayer")
+ val bgColorLayer = ComponentNameMatcher("", "colorBackgroundLayer")
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
testSpec.assertLayers {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index a9fb0f261465..181767b3448d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -30,7 +30,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.Rect
import org.junit.Assume
import org.junit.Before
@@ -178,7 +178,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(
testSpec.assertWm {
this.isAppWindowInvisible(testApp1)
.then()
- .isAppWindowVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp1)
}
@@ -238,9 +238,9 @@ open class QuickSwitchBetweenTwoAppsBackTest(
this.isAppWindowVisible(testApp2)
.then()
// TODO: Do we actually want to test this? Seems too implementation specific...
- .isAppWindowVisible(ComponentMatcher.LAUNCHER, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
.then()
- .isAppWindowVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp1)
}
@@ -257,9 +257,9 @@ open class QuickSwitchBetweenTwoAppsBackTest(
testSpec.assertLayers {
this.isVisible(testApp2)
.then()
- .isVisible(ComponentMatcher.LAUNCHER, isOptional = true)
+ .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp1)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
index 2607ee5bb0ef..461bae482299 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -24,7 +24,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarWindowIsVisibleAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 3b602126399d..0f05622c81bc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -31,7 +31,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.Rect
import org.junit.Assume
import org.junit.Before
@@ -112,7 +112,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
@Test
open fun startsWithApp1WindowsCoverFullScreen() {
testSpec.assertWmStart {
- this.visibleRegion(testApp1.or(ComponentMatcher.LETTERBOX))
+ this.visibleRegion(testApp1.or(ComponentNameMatcher.LETTERBOX))
.coversExactly(startDisplayBounds)
}
}
@@ -160,7 +160,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
@Test
open fun endsWithApp2LayersCoveringFullScreen() {
testSpec.assertLayersEnd {
- this.visibleRegion(testApp2.or(ComponentMatcher.LETTERBOX))
+ this.visibleRegion(testApp2.or(ComponentNameMatcher.LETTERBOX))
.coversExactly(startDisplayBounds)
}
}
@@ -187,7 +187,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
testSpec.assertWm {
this.isAppWindowInvisible(testApp2)
.then()
- .isAppWindowVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp2)
}
@@ -246,9 +246,9 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
testSpec.assertWm {
this.isAppWindowVisible(testApp1)
.then()
- .isAppWindowVisible(ComponentMatcher.LAUNCHER, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
.then()
- .isAppWindowVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp2)
}
@@ -265,9 +265,9 @@ open class QuickSwitchBetweenTwoAppsForwardTest(
testSpec.assertLayers {
this.isVisible(testApp1)
.then()
- .isVisible(ComponentMatcher.LAUNCHER, isOptional = true)
+ .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp2)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
index 27ae12566e94..f644b97e03df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -24,7 +24,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarWindowIsVisibleAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index c79b55251c74..d1f356c830eb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -30,7 +30,7 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.navBarWindowIsVisibleAtStartAndEnd
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.Rect
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -152,7 +152,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun startsWithLauncherWindowsCoverFullScreen() {
testSpec.assertWmStart {
- this.visibleRegion(ComponentMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
}
}
@@ -164,7 +164,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun startsWithLauncherLayersCoverFullScreen() {
testSpec.assertLayersStart {
- this.visibleRegion(ComponentMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
}
}
@@ -175,7 +175,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun startsWithLauncherBeingOnTop() {
testSpec.assertWmStart {
- this.isAppWindowOnTop(ComponentMatcher.LAUNCHER)
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
}
}
@@ -228,9 +228,9 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun launcherWindowBecomesAndStaysInvisible() {
testSpec.assertWm {
- this.isAppWindowOnTop(ComponentMatcher.LAUNCHER)
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
.then()
- .isAppWindowNotOnTop(ComponentMatcher.LAUNCHER)
+ .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER)
}
}
@@ -243,9 +243,9 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun launcherLayerBecomesAndStaysInvisible() {
testSpec.assertLayers {
- this.isVisible(ComponentMatcher.LAUNCHER)
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
.then()
- .isInvisible(ComponentMatcher.LAUNCHER)
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
}
}
@@ -258,9 +258,9 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
testSpec.assertWm {
- this.isAppWindowOnTop(ComponentMatcher.LAUNCHER)
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
.then()
- .isAppWindowVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp)
}
@@ -275,9 +275,9 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes
@Test
fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
testSpec.assertLayers {
- this.isVisible(ComponentMatcher.LAUNCHER)
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
.then()
- .isVisible(ComponentMatcher.SNAPSHOT, isOptional = true)
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 5e80fabcd8a4..4be8963bf7b7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,7 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -106,10 +106,10 @@ class ChangeAppRotationTest(
testSpec.assertLayers {
this.isVisible(testApp)
.then()
- .isVisible(ComponentMatcher.ROTATION)
+ .isVisible(ComponentNameMatcher.ROTATION)
.then()
.isVisible(testApp)
- .isInvisible(ComponentMatcher.ROTATION)
+ .isInvisible(ComponentNameMatcher.ROTATION)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 36a152117dcc..7e159d465b13 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -22,7 +22,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.Test
/**
@@ -55,9 +55,9 @@ abstract class RotationTransition(testSpec: FlickerTestParameter) : BaseTest(tes
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
ignoreLayers = listOf(
- ComponentMatcher.SPLASH_SCREEN,
- ComponentMatcher.SNAPSHOT,
- ComponentMatcher("", "SecondaryHomeHandle")
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher("", "SecondaryHomeHandle")
)
)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 1e3caa43a07c..0912812afef9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.ComponentNameMatcher
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
@@ -186,7 +186,7 @@ open class SeamlessAppRotationTest(
@Test
fun statusBarWindowIsAlwaysInvisible() {
testSpec.assertWm {
- this.isAboveAppWindowInvisible(ComponentMatcher.STATUS_BAR)
+ this.isAboveAppWindowInvisible(ComponentNameMatcher.STATUS_BAR)
}
}
@@ -198,7 +198,7 @@ open class SeamlessAppRotationTest(
@Test
fun statusBarLayerIsAlwaysInvisible() {
testSpec.assertLayers {
- this.isInvisible(ComponentMatcher.STATUS_BAR)
+ this.isInvisible(ComponentNameMatcher.STATUS_BAR)
}
}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 39ac24b29f35..b3165d320e9b 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1981,10 +1981,11 @@ class Action : public ManifestExtractor::Element {
if (ElementCast<Activity>(parent_stack[1])) {
// Detects the presence of a particular type of activity.
Activity* activity = ElementCast<Activity>(parent_stack[1]);
- auto map = std::map<std::string, std::string>({
- { "android.intent.action.MAIN" , "main" },
- { "android.intent.action.VIDEO_CAMERA" , "camera" },
- { "android.intent.action.STILL_IMAGE_CAMERA_SECURE" , "camera-secure" },
+ static const auto map = std::map<std::string, std::string>({
+ {"android.intent.action.MAIN", "main"},
+ {"android.media.action.VIDEO_CAMERA", "camera"},
+ {"android.media.action.STILL_IMAGE_CAMERA", "camera"},
+ {"android.media.action.STILL_IMAGE_CAMERA_SECURE", "camera-secure"},
});
auto entry = map.find(action);
@@ -2735,10 +2736,9 @@ bool ManifestExtractor::Extract(android::IDiagnostics* diag) {
auto it = apk_->GetFileCollection()->Iterator();
while (it->HasNext()) {
auto file_path = it->Next()->GetSource().path;
- size_t pos = file_path.find("lib/");
- if (pos != std::string::npos) {
- file_path = file_path.substr(pos + 4);
- pos = file_path.find('/');
+ if (file_path.starts_with("lib/")) {
+ file_path = file_path.substr(4);
+ size_t pos = file_path.find('/');
if (pos != std::string::npos) {
file_path = file_path.substr(0, pos);
}
diff --git a/tools/aapt2/integration-tests/DumpTest/components.apk b/tools/aapt2/integration-tests/DumpTest/components.apk
index deb55ea6fda3..a34ec83f3dae 100644
--- a/tools/aapt2/integration-tests/DumpTest/components.apk
+++ b/tools/aapt2/integration-tests/DumpTest/components.apk
Binary files differ
diff --git a/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt b/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
index bd7673616ddc..8e733a5db034 100644
--- a/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
+++ b/tools/aapt2/integration-tests/DumpTest/components_full_proto.txt
@@ -1677,7 +1677,7 @@ xml_files {
attribute {
namespace_uri: "http://schemas.android.com/apk/res/android"
name: "name"
- value: "android.intent.action.VIDEO_CAMERA"
+ value: "android.media.action.VIDEO_CAMERA"
resource_id: 16842755
}
}
@@ -1691,7 +1691,7 @@ xml_files {
attribute {
namespace_uri: "http://schemas.android.com/apk/res/android"
name: "name"
- value: "android.intent.action.STILL_IMAGE_CAMERA_SECURE"
+ value: "android.media.action.STILL_IMAGE_CAMERA_SECURE"
resource_id: 16842755
}
}
diff --git a/tools/lint/Android.bp b/tools/lint/Android.bp
index 260104145505..96618f413db1 100644
--- a/tools/lint/Android.bp
+++ b/tools/lint/Android.bp
@@ -51,3 +51,9 @@ java_test_host {
unit_test: true,
},
}
+
+python_binary_host {
+ name: "lint_fix",
+ main: "fix/lint_fix.py",
+ srcs: ["fix/lint_fix.py"],
+}
diff --git a/tools/lint/README.md b/tools/lint/README.md
index c674d36431b7..99149c140c1c 100644
--- a/tools/lint/README.md
+++ b/tools/lint/README.md
@@ -44,6 +44,10 @@ m out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/a
environment variable with the id of the lint. For example:
`ANDROID_LINT_CHECK=UnusedTokenOfOriginalCallingIdentity m out/[...]/lint-report.html`
+## How to apply automatic fixes suggested by lint
+
+See [lint_fix](fix/README.md)
+
## Create or update a baseline
Baseline files can be used to silence known errors (and warnings) that are deemed to be safe. When
diff --git a/tools/lint/fix/README.md b/tools/lint/fix/README.md
new file mode 100644
index 000000000000..367d0bcb1aa7
--- /dev/null
+++ b/tools/lint/fix/README.md
@@ -0,0 +1,46 @@
+# Refactoring the platform with lint
+Inspiration: go/refactor-the-platform-with-lint\
+**Special Thanks: brufino@, azharaa@, for the prior work that made this all possible**
+
+## What is this?
+
+It's a python script that runs the framework linter,
+and then copies modified files back into the source tree.\
+Why python, you ask? Because python is cool ¯\_(ツ)_/¯.
+
+## Why?
+
+Lint is not allowed to modify source files directly via lint's `--apply-suggestions` flag.
+As a compromise, soong zips up the (potentially) modified sources and leaves them in an intermediate
+directory. This script runs the lint, unpacks those files, and copies them back into the tree.
+
+## How do I run it?
+**WARNING: You probably want to commit/stash any changes to your working tree before doing this...**
+
+From this directory, run `python lint_fix.py -h`.
+The script's help output explains things that are omitted here.
+
+Alternatively, there is a python binary target you can build to make this
+available anywhere in your tree:
+```
+m lint_fix
+lint_fix -h
+```
+
+**Gotcha**: You must have run `source build/envsetup.sh` and `lunch` first.
+
+Example: `lint_fix frameworks/base/services/core/services.core.unboosted UseEnforcePermissionAnnotation --dry-run`
+```shell
+(
+export ANDROID_LINT_CHECK=UseEnforcePermissionAnnotation;
+cd $ANDROID_BUILD_TOP;
+source build/envsetup.sh;
+rm out/soong/.intermediates/frameworks/base/services/core/services.core.unboosted/android_common/lint/lint-report.html;
+m out/soong/.intermediates/frameworks/base/services/core/services.core.unboosted/android_common/lint/lint-report.html;
+cd out/soong/.intermediates/frameworks/base/services/core/services.core.unboosted/android_common/lint;
+unzip suggested-fixes.zip -d suggested-fixes;
+cd suggested-fixes;
+find . -path ./out -prune -o -name '*.java' -print | xargs -n 1 sh -c 'cp $1 $ANDROID_BUILD_TOP/$1' --;
+rm -rf suggested-fixes
+)
+```
diff --git a/tools/lint/fix/lint_fix.py b/tools/lint/fix/lint_fix.py
new file mode 100644
index 000000000000..3ff8131489ff
--- /dev/null
+++ b/tools/lint/fix/lint_fix.py
@@ -0,0 +1,76 @@
+import argparse
+import os
+import sys
+
+ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
+PATH_PREFIX = "out/soong/.intermediates"
+PATH_SUFFIX = "android_common/lint"
+FIX_DIR = "suggested-fixes"
+
+parser = argparse.ArgumentParser(description="""
+This is a python script that applies lint fixes to the platform:
+1. Set up the environment, etc.
+2. Build the lint and run it.
+3. Unpack soong's intermediate zip containing source files modified by lint.
+4. Copy the modified files back into the tree.
+
+**Gotcha**: You must have run `source build/envsetup.sh` and `lunch` \
+so that the `ANDROID_BUILD_TOP` environment variable has been set.
+Alternatively, set it manually in your shell.
+""", formatter_class=argparse.RawTextHelpFormatter)
+
+parser.add_argument('build_path', metavar='build_path', type=str,
+ help='The build module to run '
+ '(e.g. frameworks/base/framework-minus-apex or '
+ 'frameworks/base/services/core/services.core.unboosted)')
+
+parser.add_argument('--check', metavar='check', type=str,
+ help='Which lint to run. Passed to the ANDROID_LINT_CHECK environment variable.')
+
+parser.add_argument('--dry-run', dest='dry_run', action='store_true',
+ help='Just print the resulting shell script instead of running it.')
+
+parser.add_argument('--no-fix', dest='no_fix', action='store_true',
+ help='Just build and run the lint, do NOT apply the fixes.')
+
+args = parser.parse_args()
+
+path = f"{PATH_PREFIX}/{args.build_path}/{PATH_SUFFIX}"
+target = f"{path}/lint-report.html"
+
+commands = []
+
+if not args.dry_run:
+ commands += [f"export ANDROID_BUILD_TOP={ANDROID_BUILD_TOP}"]
+
+if args.check:
+ commands += [f"export ANDROID_LINT_CHECK={args.check}"]
+
+commands += [
+ "cd $ANDROID_BUILD_TOP",
+ "source build/envsetup.sh",
+ f"rm {target}", # remove the file first so soong doesn't think there is no work to do
+ f"m {target}",
+]
+
+if not args.no_fix:
+ commands += [
+ f"cd {path}",
+ f"unzip {FIX_DIR}.zip -d {FIX_DIR}",
+ f"cd {FIX_DIR}",
+ # Find all the java files in the fix directory, excluding the ./out subdirectory,
+ # and copy them back into the same path within the tree.
+ f"find . -path ./out -prune -o -name '*.java' -print | xargs -n 1 sh -c 'cp $1 $ANDROID_BUILD_TOP/$1' --",
+ f"rm -rf {FIX_DIR}"
+ ]
+
+if args.dry_run:
+ print("(\n" + ";\n".join(commands) + "\n)")
+ sys.exit(0)
+
+with_echo = []
+for c in commands:
+ with_echo.append(f'echo "{c}"')
+ with_echo.append(c)
+
+os.system("(\n" + ";\n".join(with_echo) + "\n)")