summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java2
-rw-r--r--boot/boot-image-profile.txt4
-rw-r--r--boot/preloaded-classes4
-rw-r--r--config/boot-image-profile.txt4
-rw-r--r--config/preloaded-classes4
-rw-r--r--core/api/current.txt250
-rw-r--r--core/api/system-current.txt18
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/java/android/app/AppOpsManager.java19
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java1
-rw-r--r--core/java/android/app/IWallpaperManager.aidl14
-rw-r--r--core/java/android/app/Instrumentation.java3
-rw-r--r--core/java/android/app/TEST_MAPPING3
-rw-r--r--core/java/android/app/UiAutomation.java132
-rw-r--r--core/java/android/app/UiAutomationConnection.java11
-rw-r--r--core/java/android/credentials/ui/CreateCredentialProviderData.java13
-rw-r--r--core/java/android/credentials/ui/GetCredentialProviderData.java41
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java24
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java4
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java10
-rw-r--r--core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl30
-rwxr-xr-xcore/java/android/os/Build.java4
-rw-r--r--core/java/android/provider/Telephony.java48
-rw-r--r--core/java/android/service/credentials/BeginCreateCredentialResponse.java21
-rw-r--r--core/java/android/service/credentials/BeginGetCredentialResponse.java60
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java16
-rw-r--r--core/java/android/service/voice/SoftwareHotwordDetector.java15
-rw-r--r--core/java/android/service/voice/SoundTriggerFailure.java21
-rw-r--r--core/java/android/service/voice/VisualQueryDetector.java11
-rw-r--r--core/java/android/service/wallpaper/IWallpaperEngine.aidl2
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java47
-rw-r--r--core/java/android/util/FeatureFlagUtils.java7
-rw-r--r--core/java/android/util/TimeUtils.java9
-rw-r--r--core/java/android/view/Choreographer.java27
-rw-r--r--core/java/android/view/InputEvent.java3
-rw-r--r--core/java/android/view/KeyEvent.java1
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java1
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/window/BackMotionEvent.java53
-rw-r--r--core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl17
-rw-r--r--core/java/com/android/internal/expresslog/Counter.java71
-rw-r--r--core/java/com/android/internal/expresslog/Histogram.java234
-rw-r--r--core/java/com/android/internal/expresslog/OWNERS1
-rw-r--r--core/java/com/android/internal/expresslog/TEST_MAPPING12
-rw-r--r--core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java2
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/OWNERS3
-rw-r--r--core/jni/android_media_AudioSystem.cpp49
-rw-r--r--core/jni/com_android_internal_expresslog_Utils.cpp57
-rw-r--r--core/proto/android/server/activitymanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml31
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/public-final.xml339
-rw-r--r--core/res/res/values/public-staging.xml204
-rw-r--r--core/res/res/values/strings.xml10
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/app/UiAutomationTest.java144
-rw-r--r--core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java102
-rw-r--r--core/tests/coretests/src/android/hardware/biometrics/OWNERS1
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java8
-rw-r--r--core/tests/expresslog/AndroidManifest.xml29
-rw-r--r--core/tests/expresslog/OWNERS3
-rw-r--r--core/tests/expresslog/TEST_MAPPING12
-rw-r--r--core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java167
-rw-r--r--core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java125
-rw-r--r--data/etc/preinstalled-packages-platform-overlays.xml3
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java41
-rw-r--r--libs/WindowManager/Jetpack/Android.bp18
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java10
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-core-release.aarbin3714 -> 0 bytes
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin41055 -> 0 bytes
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java94
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java25
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java25
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java5
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp6
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerDetector.java43
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt14
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt12
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt50
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt14
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt5
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt1
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt36
-rw-r--r--packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java38
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt57
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt8
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt1
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt163
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt12
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java25
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java51
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/AndroidManifest.xml8
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt1
-rw-r--r--packages/SystemUI/res/drawable/chipbar_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/chipbar_end_button_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/hearing.xml24
-rw-r--r--packages/SystemUI/res/layout/chipbar.xml12
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/styles.xml9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java196
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceDetectTableLog.kt (renamed from core/java/com/android/internal/expresslog/Utils.java)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt200
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHost.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt300
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt292
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt176
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java18
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt8
-rw-r--r--packages/overlays/Android.mk1
-rw-r--r--packages/overlays/NotesRoleEnabledOverlay/Android.bp (renamed from core/tests/expresslog/Android.bp)33
-rw-r--r--packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml26
-rw-r--r--packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml28
-rw-r--r--packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml22
-rw-r--r--proto/src/system_messages.proto2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java122
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java38
-rw-r--r--services/core/java/com/android/server/BinaryTransparencyService.java2
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java30
-rw-r--r--services/core/java/com/android/server/RescueParty.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java9
-rw-r--r--services/core/java/com/android/server/am/AppExitInfoTracker.java175
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java207
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java118
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java126
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java23
-rw-r--r--services/core/java/com/android/server/am/DropboxRateLimiter.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java11
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java2
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java4
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java15
-rw-r--r--services/core/java/com/android/server/notification/GroupHelper.java241
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java74
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java57
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java21
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java29
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java48
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java57
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java11
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java40
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java8
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java4
-rw-r--r--services/core/java/com/android/server/wm/Transition.java27
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java20
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java22
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java470
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java16
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java22
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java71
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java44
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java238
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java (renamed from services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java)103
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java24
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java493
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java83
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java5
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaDevice.java256
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java3
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java147
-rw-r--r--services/voiceinteraction/TEST_MAPPING3
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java62
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java28
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java28
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java4
-rw-r--r--telephony/java/android/telephony/satellite/AntennaDirection.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/AntennaDirection.java138
-rw-r--r--telephony/java/android/telephony/satellite/AntennaPosition.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/AntennaPosition.java117
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteCapabilities.java68
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java40
-rw-r--r--telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl12
-rw-r--r--tests/FlickerTests/AndroidTest.xml1
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java46
-rw-r--r--tests/testables/tests/AndroidManifest.xml2
-rw-r--r--tests/testables/tests/AndroidTest.xml27
-rw-r--r--tools/aapt/SdkConstants.h1
-rw-r--r--tools/aapt2/SdkConstants.h1
351 files changed, 8024 insertions, 3583 deletions
diff --git a/Android.bp b/Android.bp
index cff863b44499..64d2c66f013c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -410,6 +410,7 @@ java_defaults {
"spatializer-aidl-java",
"audiopolicy-aidl-java",
"sounddose-aidl-java",
+ "modules-utils-expresslog",
],
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index aef9dd058658..3aec8ba39a35 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1588,12 +1588,12 @@ public class JobSchedulerService extends com.android.server.SystemService
final ArrayMap<String, List<JobInfo>> outMap = new ArrayMap<>();
synchronized (mLock) {
ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid);
- // Write out for loop to avoid addAll() creating an Iterator.
+ // Write out for loop to avoid creating an Iterator.
for (int i = jobs.size() - 1; i >= 0; i--) {
final JobStatus job = jobs.valueAt(i);
List<JobInfo> outList = outMap.get(job.getNamespace());
if (outList == null) {
- outList = new ArrayList<JobInfo>(jobs.size());
+ outList = new ArrayList<>();
outMap.put(job.getNamespace(), outList);
}
@@ -1606,7 +1606,7 @@ public class JobSchedulerService extends com.android.server.SystemService
private List<JobInfo> getPendingJobsInNamespace(int uid, @Nullable String namespace) {
synchronized (mLock) {
ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid);
- ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
+ ArrayList<JobInfo> outList = new ArrayList<>();
// Write out for loop to avoid addAll() creating an Iterator.
for (int i = jobs.size() - 1; i >= 0; i--) {
final JobStatus job = jobs.valueAt(i);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index fc6022859f5f..ba62e96b2a32 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -31,7 +31,7 @@ import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.expresslog.Counter;
+import com.android.modules.expresslog.Counter;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
index 996c3882701d..aebace57aa2c 100644
--- a/boot/boot-image-profile.txt
+++ b/boot/boot-image-profile.txt
@@ -33583,8 +33583,8 @@ Lcom/android/internal/dynamicanimation/animation/DynamicAnimation;
Lcom/android/internal/dynamicanimation/animation/Force;
Lcom/android/internal/dynamicanimation/animation/SpringAnimation;
Lcom/android/internal/dynamicanimation/animation/SpringForce;
-Lcom/android/internal/expresslog/Counter;
-Lcom/android/internal/expresslog/Utils;
+Lcom/android/modules/expresslog/Counter;
+Lcom/android/modules/expresslog/Utils;
Lcom/android/internal/graphics/ColorUtils$ContrastCalculator;
Lcom/android/internal/graphics/ColorUtils;
Lcom/android/internal/graphics/SfVsyncFrameCallbackProvider;
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index 21ae13474d84..4293caf57aea 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -10784,8 +10784,8 @@ com.android.internal.dynamicanimation.animation.DynamicAnimation
com.android.internal.dynamicanimation.animation.Force
com.android.internal.dynamicanimation.animation.SpringAnimation
com.android.internal.dynamicanimation.animation.SpringForce
-com.android.internal.expresslog.Counter
-com.android.internal.expresslog.Utils
+com.android.modules.expresslog.Counter
+com.android.modules.expresslog.Utils
com.android.internal.graphics.ColorUtils$ContrastCalculator
com.android.internal.graphics.ColorUtils
com.android.internal.graphics.SfVsyncFrameCallbackProvider
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 3cc990873c87..bb0748764cc8 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -43717,8 +43717,8 @@ Lcom/android/internal/dynamicanimation/animation/DynamicAnimation;
Lcom/android/internal/dynamicanimation/animation/Force;
Lcom/android/internal/dynamicanimation/animation/SpringAnimation;
Lcom/android/internal/dynamicanimation/animation/SpringForce;
-Lcom/android/internal/expresslog/Counter;
-Lcom/android/internal/expresslog/Utils;
+Lcom/android/modules/expresslog/Counter;
+Lcom/android/modules/expresslog/Utils;
Lcom/android/internal/graphics/ColorUtils$ContrastCalculator;
Lcom/android/internal/graphics/ColorUtils;
Lcom/android/internal/graphics/SfVsyncFrameCallbackProvider;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 8e50fe8e4e0f..1812c2bb61d6 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -10815,8 +10815,8 @@ com.android.internal.dynamicanimation.animation.DynamicAnimation
com.android.internal.dynamicanimation.animation.Force
com.android.internal.dynamicanimation.animation.SpringAnimation
com.android.internal.dynamicanimation.animation.SpringForce
-com.android.internal.expresslog.Counter
-com.android.internal.expresslog.Utils
+com.android.modules.expresslog.Counter
+com.android.modules.expresslog.Utils
com.android.internal.graphics.ColorUtils$ContrastCalculator
com.android.internal.graphics.ColorUtils
com.android.internal.graphics.SfVsyncFrameCallbackProvider
diff --git a/core/api/current.txt b/core/api/current.txt
index e10fbf20b772..288ab479c0fb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -377,7 +377,7 @@ package android {
public static final class R.attr {
ctor public R.attr();
field public static final int absListViewStyle = 16842858; // 0x101006a
- field public static final int accessibilityDataSensitive;
+ field public static final int accessibilityDataSensitive = 16844407; // 0x1010677
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
field public static final int accessibilityFlags = 16843652; // 0x1010384
@@ -445,12 +445,12 @@ package android {
field public static final int allowGameFpsOverride = 16844378; // 0x101065a
field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
field public static final int allowParallelSyncs = 16843570; // 0x1010332
- field public static final int allowSharedIsolatedProcess;
+ field public static final int allowSharedIsolatedProcess = 16844413; // 0x101067d
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
field public static final int allowUndo = 16843999; // 0x10104df
field public static final int allowUntrustedActivityEmbedding = 16844393; // 0x1010669
- field public static final int allowUpdateOwnership;
+ field public static final int allowUpdateOwnership = 16844416; // 0x1010680
field public static final int alpha = 16843551; // 0x101031f
field public static final int alphabeticModifiers = 16844110; // 0x101054e
field public static final int alphabeticShortcut = 16843235; // 0x10101e3
@@ -556,7 +556,7 @@ package android {
field public static final int canTakeScreenshot = 16844303; // 0x101060f
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
field public static final int cantSaveState = 16844142; // 0x101056e
- field public static final int capability;
+ field public static final int capability = 16844423; // 0x1010687
field @Deprecated public static final int capitalize = 16843113; // 0x1010169
field public static final int category = 16843752; // 0x10103e8
field public static final int centerBright = 16842956; // 0x10100cc
@@ -741,7 +741,7 @@ package android {
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c
- field public static final int enableTextStylingShortcuts;
+ field public static final int enableTextStylingShortcuts = 16844408; // 0x1010678
field public static final int enableVrMode = 16844069; // 0x1010525
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
@@ -810,7 +810,7 @@ package android {
field public static final int focusableInTouchMode = 16842971; // 0x10100db
field public static final int focusedByDefault = 16844100; // 0x1010544
field @Deprecated public static final int focusedMonthDateColor = 16843587; // 0x1010343
- field public static final int focusedSearchResultHighlightColor;
+ field public static final int focusedSearchResultHighlightColor = 16844419; // 0x1010683
field public static final int font = 16844082; // 0x1010532
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
@@ -896,10 +896,10 @@ package android {
field public static final int hand_secondTintMode = 16844349; // 0x101063d
field public static final int handle = 16843354; // 0x101025a
field public static final int handleProfiling = 16842786; // 0x1010022
- field public static final int handwritingBoundsOffsetBottom;
- field public static final int handwritingBoundsOffsetLeft;
- field public static final int handwritingBoundsOffsetRight;
- field public static final int handwritingBoundsOffsetTop;
+ field public static final int handwritingBoundsOffsetBottom = 16844406; // 0x1010676
+ field public static final int handwritingBoundsOffsetLeft = 16844403; // 0x1010673
+ field public static final int handwritingBoundsOffsetRight = 16844405; // 0x1010675
+ field public static final int handwritingBoundsOffsetTop = 16844404; // 0x1010674
field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
field public static final int hardwareAccelerated = 16843475; // 0x10102d3
field public static final int hasCode = 16842764; // 0x101000c
@@ -986,7 +986,7 @@ package android {
field public static final int isAlwaysSyncable = 16843571; // 0x1010333
field public static final int isAsciiCapable = 16843753; // 0x10103e9
field public static final int isAuxiliary = 16843647; // 0x101037f
- field public static final int isCredential;
+ field public static final int isCredential = 16844417; // 0x1010681
field public static final int isDefault = 16843297; // 0x1010221
field public static final int isFeatureSplit = 16844123; // 0x101055b
field public static final int isGame = 16843764; // 0x10103f4
@@ -1021,8 +1021,8 @@ package android {
field @Deprecated public static final int keyTextSize = 16843316; // 0x1010234
field @Deprecated public static final int keyWidth = 16843325; // 0x101023d
field public static final int keyboardLayout = 16843691; // 0x10103ab
- field public static final int keyboardLayoutType;
- field public static final int keyboardLocale;
+ field public static final int keyboardLayoutType = 16844415; // 0x101067f
+ field public static final int keyboardLocale = 16844414; // 0x101067e
field @Deprecated public static final int keyboardMode = 16843341; // 0x101024d
field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
field public static final int keycode = 16842949; // 0x10100c5
@@ -1263,8 +1263,8 @@ package android {
field public static final int persistentDrawingCache = 16842990; // 0x10100ee
field public static final int persistentWhenFeatureAvailable = 16844131; // 0x1010563
field @Deprecated public static final int phoneNumber = 16843111; // 0x1010167
- field public static final int physicalKeyboardHintLanguageTag;
- field public static final int physicalKeyboardHintLayoutType;
+ field public static final int physicalKeyboardHintLanguageTag = 16844411; // 0x101067b
+ field public static final int physicalKeyboardHintLayoutType = 16844412; // 0x101067c
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
field public static final int pointerIcon = 16844041; // 0x1010509
@@ -1354,7 +1354,7 @@ package android {
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
- field public static final int requiredDisplayCategory;
+ field public static final int requiredDisplayCategory = 16844409; // 0x1010679
field public static final int requiredFeature = 16844116; // 0x1010554
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiredNotFeature = 16844117; // 0x1010555
@@ -1422,7 +1422,7 @@ package android {
field public static final int searchHintIcon = 16843988; // 0x10104d4
field public static final int searchIcon = 16843907; // 0x1010483
field public static final int searchMode = 16843221; // 0x10101d5
- field public static final int searchResultHighlightColor;
+ field public static final int searchResultHighlightColor = 16844418; // 0x1010682
field public static final int searchSettingsDescription = 16843402; // 0x101028a
field public static final int searchSuggestAuthority = 16843222; // 0x10101d6
field public static final int searchSuggestIntentAction = 16843225; // 0x10101d9
@@ -1449,7 +1449,7 @@ package android {
field public static final int sessionService = 16843837; // 0x101043d
field public static final int settingsActivity = 16843301; // 0x1010225
field public static final int settingsSliceUri = 16844179; // 0x1010593
- field public static final int settingsSubtitle;
+ field public static final int settingsSubtitle = 16844422; // 0x1010686
field public static final int setupActivity = 16843766; // 0x10103f6
field public static final int shadowColor = 16843105; // 0x1010161
field public static final int shadowDx = 16843106; // 0x1010162
@@ -1555,7 +1555,7 @@ package android {
field public static final int strokeLineJoin = 16843788; // 0x101040c
field public static final int strokeMiterLimit = 16843789; // 0x101040d
field public static final int strokeWidth = 16843783; // 0x1010407
- field public static final int stylusHandwritingSettingsActivity;
+ field public static final int stylusHandwritingSettingsActivity = 16844420; // 0x1010684
field public static final int subMenuArrow = 16844019; // 0x10104f3
field public static final int submitBackground = 16843912; // 0x1010488
field public static final int subtitle = 16843473; // 0x10102d1
@@ -1861,7 +1861,7 @@ package android {
field public static final int windowMinWidthMajor = 16843606; // 0x1010356
field public static final int windowMinWidthMinor = 16843607; // 0x1010357
field public static final int windowNoDisplay = 16843294; // 0x101021e
- field public static final int windowNoMoveAnimation;
+ field public static final int windowNoMoveAnimation = 16844421; // 0x1010685
field public static final int windowNoTitle = 16842838; // 0x1010056
field @Deprecated public static final int windowOverscan = 16843727; // 0x10103cf
field public static final int windowReenterTransition = 16843951; // 0x10104af
@@ -1966,18 +1966,18 @@ package android {
field public static final int system_accent3_700 = 17170522; // 0x106005a
field public static final int system_accent3_800 = 17170523; // 0x106005b
field public static final int system_accent3_900 = 17170524; // 0x106005c
- field public static final int system_background_dark;
- field public static final int system_background_light;
- field public static final int system_control_activated_dark;
- field public static final int system_control_activated_light;
- field public static final int system_control_highlight_dark;
- field public static final int system_control_highlight_light;
- field public static final int system_control_normal_dark;
- field public static final int system_control_normal_light;
- field public static final int system_error_container_dark;
- field public static final int system_error_container_light;
- field public static final int system_error_dark;
- field public static final int system_error_light;
+ field public static final int system_background_dark = 17170581; // 0x1060095
+ field public static final int system_background_light = 17170538; // 0x106006a
+ field public static final int system_control_activated_dark = 17170599; // 0x10600a7
+ field public static final int system_control_activated_light = 17170556; // 0x106007c
+ field public static final int system_control_highlight_dark = 17170601; // 0x10600a9
+ field public static final int system_control_highlight_light = 17170558; // 0x106007e
+ field public static final int system_control_normal_dark = 17170600; // 0x10600a8
+ field public static final int system_control_normal_light = 17170557; // 0x106007d
+ field public static final int system_error_container_dark = 17170597; // 0x10600a5
+ field public static final int system_error_container_light = 17170554; // 0x106007a
+ field public static final int system_error_dark = 17170595; // 0x10600a3
+ field public static final int system_error_light = 17170552; // 0x1060078
field public static final int system_neutral1_0 = 17170461; // 0x106001d
field public static final int system_neutral1_10 = 17170462; // 0x106001e
field public static final int system_neutral1_100 = 17170464; // 0x1060020
@@ -2004,94 +2004,94 @@ package android {
field public static final int system_neutral2_700 = 17170483; // 0x1060033
field public static final int system_neutral2_800 = 17170484; // 0x1060034
field public static final int system_neutral2_900 = 17170485; // 0x1060035
- field public static final int system_on_background_dark;
- field public static final int system_on_background_light;
- field public static final int system_on_error_container_dark;
- field public static final int system_on_error_container_light;
- field public static final int system_on_error_dark;
- field public static final int system_on_error_light;
- field public static final int system_on_primary_container_dark;
- field public static final int system_on_primary_container_light;
- field public static final int system_on_primary_dark;
- field public static final int system_on_primary_fixed;
- field public static final int system_on_primary_fixed_variant;
- field public static final int system_on_primary_light;
- field public static final int system_on_secondary_container_dark;
- field public static final int system_on_secondary_container_light;
- field public static final int system_on_secondary_dark;
- field public static final int system_on_secondary_fixed;
- field public static final int system_on_secondary_fixed_variant;
- field public static final int system_on_secondary_light;
- field public static final int system_on_surface_dark;
- field public static final int system_on_surface_light;
- field public static final int system_on_surface_variant_dark;
- field public static final int system_on_surface_variant_light;
- field public static final int system_on_tertiary_container_dark;
- field public static final int system_on_tertiary_container_light;
- field public static final int system_on_tertiary_dark;
- field public static final int system_on_tertiary_fixed;
- field public static final int system_on_tertiary_fixed_variant;
- field public static final int system_on_tertiary_light;
- field public static final int system_outline_dark;
- field public static final int system_outline_light;
- field public static final int system_outline_variant_dark;
- field public static final int system_outline_variant_light;
- field public static final int system_palette_key_color_neutral_dark;
- field public static final int system_palette_key_color_neutral_light;
- field public static final int system_palette_key_color_neutral_variant_dark;
- field public static final int system_palette_key_color_neutral_variant_light;
- field public static final int system_palette_key_color_primary_dark;
- field public static final int system_palette_key_color_primary_light;
- field public static final int system_palette_key_color_secondary_dark;
- field public static final int system_palette_key_color_secondary_light;
- field public static final int system_palette_key_color_tertiary_dark;
- field public static final int system_palette_key_color_tertiary_light;
- field public static final int system_primary_container_dark;
- field public static final int system_primary_container_light;
- field public static final int system_primary_dark;
- field public static final int system_primary_fixed;
- field public static final int system_primary_fixed_dim;
- field public static final int system_primary_light;
- field public static final int system_secondary_container_dark;
- field public static final int system_secondary_container_light;
- field public static final int system_secondary_dark;
- field public static final int system_secondary_fixed;
- field public static final int system_secondary_fixed_dim;
- field public static final int system_secondary_light;
- field public static final int system_surface_bright_dark;
- field public static final int system_surface_bright_light;
- field public static final int system_surface_container_dark;
- field public static final int system_surface_container_high_dark;
- field public static final int system_surface_container_high_light;
- field public static final int system_surface_container_highest_dark;
- field public static final int system_surface_container_highest_light;
- field public static final int system_surface_container_light;
- field public static final int system_surface_container_low_dark;
- field public static final int system_surface_container_low_light;
- field public static final int system_surface_container_lowest_dark;
- field public static final int system_surface_container_lowest_light;
- field public static final int system_surface_dark;
- field public static final int system_surface_dim_dark;
- field public static final int system_surface_dim_light;
- field public static final int system_surface_light;
- field public static final int system_surface_variant_dark;
- field public static final int system_surface_variant_light;
- field public static final int system_tertiary_container_dark;
- field public static final int system_tertiary_container_light;
- field public static final int system_tertiary_dark;
- field public static final int system_tertiary_fixed;
- field public static final int system_tertiary_fixed_dim;
- field public static final int system_tertiary_light;
- field public static final int system_text_hint_inverse_dark;
- field public static final int system_text_hint_inverse_light;
- field public static final int system_text_primary_inverse_dark;
- field public static final int system_text_primary_inverse_disable_only_dark;
- field public static final int system_text_primary_inverse_disable_only_light;
- field public static final int system_text_primary_inverse_light;
- field public static final int system_text_secondary_and_tertiary_inverse_dark;
- field public static final int system_text_secondary_and_tertiary_inverse_disabled_dark;
- field public static final int system_text_secondary_and_tertiary_inverse_disabled_light;
- field public static final int system_text_secondary_and_tertiary_inverse_light;
+ field public static final int system_on_background_dark = 17170582; // 0x1060096
+ field public static final int system_on_background_light = 17170539; // 0x106006b
+ field public static final int system_on_error_container_dark = 17170598; // 0x10600a6
+ field public static final int system_on_error_container_light = 17170555; // 0x106007b
+ field public static final int system_on_error_dark = 17170596; // 0x10600a4
+ field public static final int system_on_error_light = 17170553; // 0x1060079
+ field public static final int system_on_primary_container_dark = 17170570; // 0x106008a
+ field public static final int system_on_primary_container_light = 17170527; // 0x106005f
+ field public static final int system_on_primary_dark = 17170572; // 0x106008c
+ field public static final int system_on_primary_fixed = 17170614; // 0x10600b6
+ field public static final int system_on_primary_fixed_variant = 17170615; // 0x10600b7
+ field public static final int system_on_primary_light = 17170529; // 0x1060061
+ field public static final int system_on_secondary_container_dark = 17170574; // 0x106008e
+ field public static final int system_on_secondary_container_light = 17170531; // 0x1060063
+ field public static final int system_on_secondary_dark = 17170576; // 0x1060090
+ field public static final int system_on_secondary_fixed = 17170618; // 0x10600ba
+ field public static final int system_on_secondary_fixed_variant = 17170619; // 0x10600bb
+ field public static final int system_on_secondary_light = 17170533; // 0x1060065
+ field public static final int system_on_surface_dark = 17170584; // 0x1060098
+ field public static final int system_on_surface_light = 17170541; // 0x106006d
+ field public static final int system_on_surface_variant_dark = 17170593; // 0x10600a1
+ field public static final int system_on_surface_variant_light = 17170550; // 0x1060076
+ field public static final int system_on_tertiary_container_dark = 17170578; // 0x1060092
+ field public static final int system_on_tertiary_container_light = 17170535; // 0x1060067
+ field public static final int system_on_tertiary_dark = 17170580; // 0x1060094
+ field public static final int system_on_tertiary_fixed = 17170622; // 0x10600be
+ field public static final int system_on_tertiary_fixed_variant = 17170623; // 0x10600bf
+ field public static final int system_on_tertiary_light = 17170537; // 0x1060069
+ field public static final int system_outline_dark = 17170594; // 0x10600a2
+ field public static final int system_outline_light = 17170551; // 0x1060077
+ field public static final int system_outline_variant_dark = 17170625; // 0x10600c1
+ field public static final int system_outline_variant_light = 17170624; // 0x10600c0
+ field public static final int system_palette_key_color_neutral_dark = 17170610; // 0x10600b2
+ field public static final int system_palette_key_color_neutral_light = 17170567; // 0x1060087
+ field public static final int system_palette_key_color_neutral_variant_dark = 17170611; // 0x10600b3
+ field public static final int system_palette_key_color_neutral_variant_light = 17170568; // 0x1060088
+ field public static final int system_palette_key_color_primary_dark = 17170607; // 0x10600af
+ field public static final int system_palette_key_color_primary_light = 17170564; // 0x1060084
+ field public static final int system_palette_key_color_secondary_dark = 17170608; // 0x10600b0
+ field public static final int system_palette_key_color_secondary_light = 17170565; // 0x1060085
+ field public static final int system_palette_key_color_tertiary_dark = 17170609; // 0x10600b1
+ field public static final int system_palette_key_color_tertiary_light = 17170566; // 0x1060086
+ field public static final int system_primary_container_dark = 17170569; // 0x1060089
+ field public static final int system_primary_container_light = 17170526; // 0x106005e
+ field public static final int system_primary_dark = 17170571; // 0x106008b
+ field public static final int system_primary_fixed = 17170612; // 0x10600b4
+ field public static final int system_primary_fixed_dim = 17170613; // 0x10600b5
+ field public static final int system_primary_light = 17170528; // 0x1060060
+ field public static final int system_secondary_container_dark = 17170573; // 0x106008d
+ field public static final int system_secondary_container_light = 17170530; // 0x1060062
+ field public static final int system_secondary_dark = 17170575; // 0x106008f
+ field public static final int system_secondary_fixed = 17170616; // 0x10600b8
+ field public static final int system_secondary_fixed_dim = 17170617; // 0x10600b9
+ field public static final int system_secondary_light = 17170532; // 0x1060064
+ field public static final int system_surface_bright_dark = 17170590; // 0x106009e
+ field public static final int system_surface_bright_light = 17170547; // 0x1060073
+ field public static final int system_surface_container_dark = 17170587; // 0x106009b
+ field public static final int system_surface_container_high_dark = 17170588; // 0x106009c
+ field public static final int system_surface_container_high_light = 17170545; // 0x1060071
+ field public static final int system_surface_container_highest_dark = 17170589; // 0x106009d
+ field public static final int system_surface_container_highest_light = 17170546; // 0x1060072
+ field public static final int system_surface_container_light = 17170544; // 0x1060070
+ field public static final int system_surface_container_low_dark = 17170585; // 0x1060099
+ field public static final int system_surface_container_low_light = 17170542; // 0x106006e
+ field public static final int system_surface_container_lowest_dark = 17170586; // 0x106009a
+ field public static final int system_surface_container_lowest_light = 17170543; // 0x106006f
+ field public static final int system_surface_dark = 17170583; // 0x1060097
+ field public static final int system_surface_dim_dark = 17170591; // 0x106009f
+ field public static final int system_surface_dim_light = 17170548; // 0x1060074
+ field public static final int system_surface_light = 17170540; // 0x106006c
+ field public static final int system_surface_variant_dark = 17170592; // 0x10600a0
+ field public static final int system_surface_variant_light = 17170549; // 0x1060075
+ field public static final int system_tertiary_container_dark = 17170577; // 0x1060091
+ field public static final int system_tertiary_container_light = 17170534; // 0x1060066
+ field public static final int system_tertiary_dark = 17170579; // 0x1060093
+ field public static final int system_tertiary_fixed = 17170620; // 0x10600bc
+ field public static final int system_tertiary_fixed_dim = 17170621; // 0x10600bd
+ field public static final int system_tertiary_light = 17170536; // 0x1060068
+ field public static final int system_text_hint_inverse_dark = 17170606; // 0x10600ae
+ field public static final int system_text_hint_inverse_light = 17170563; // 0x1060083
+ field public static final int system_text_primary_inverse_dark = 17170602; // 0x10600aa
+ field public static final int system_text_primary_inverse_disable_only_dark = 17170604; // 0x10600ac
+ field public static final int system_text_primary_inverse_disable_only_light = 17170561; // 0x1060081
+ field public static final int system_text_primary_inverse_light = 17170559; // 0x106007f
+ field public static final int system_text_secondary_and_tertiary_inverse_dark = 17170603; // 0x10600ab
+ field public static final int system_text_secondary_and_tertiary_inverse_disabled_dark = 17170605; // 0x10600ad
+ field public static final int system_text_secondary_and_tertiary_inverse_disabled_light = 17170562; // 0x1060082
+ field public static final int system_text_secondary_and_tertiary_inverse_light = 17170560; // 0x1060080
field public static final int tab_indicator_text = 17170441; // 0x1060009
field @Deprecated public static final int tertiary_text_dark = 17170448; // 0x1060010
field @Deprecated public static final int tertiary_text_light = 17170449; // 0x1060011
@@ -2310,7 +2310,7 @@ package android {
field public static final int accessibilityActionPageUp = 16908358; // 0x1020046
field public static final int accessibilityActionPressAndHold = 16908362; // 0x102004a
field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
- field public static final int accessibilityActionScrollInDirection;
+ field public static final int accessibilityActionScrollInDirection = 16908382; // 0x102005e
field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
field public static final int accessibilityActionScrollToPosition = 16908343; // 0x1020037
@@ -2331,7 +2331,7 @@ package android {
field public static final int addToDictionary = 16908330; // 0x102002a
field public static final int autofill = 16908355; // 0x1020043
field public static final int background = 16908288; // 0x1020000
- field public static final int bold;
+ field public static final int bold = 16908379; // 0x102005b
field public static final int button1 = 16908313; // 0x1020019
field public static final int button2 = 16908314; // 0x102001a
field public static final int button3 = 16908315; // 0x102001b
@@ -2357,7 +2357,7 @@ package android {
field public static final int inputExtractAccessories = 16908378; // 0x102005a
field public static final int inputExtractAction = 16908377; // 0x1020059
field public static final int inputExtractEditText = 16908325; // 0x1020025
- field public static final int italic;
+ field public static final int italic = 16908380; // 0x102005c
field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
field public static final int list_container = 16908351; // 0x102003f
@@ -2389,7 +2389,7 @@ package android {
field public static final int textAssist = 16908353; // 0x1020041
field public static final int title = 16908310; // 0x1020016
field public static final int toggle = 16908311; // 0x1020017
- field public static final int underline;
+ field public static final int underline = 16908381; // 0x102005d
field public static final int undo = 16908338; // 0x1020032
field public static final int widget_frame = 16908312; // 0x1020018
}
@@ -32652,7 +32652,7 @@ package android.os {
field public static final int S = 31; // 0x1f
field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 33; // 0x21
- field public static final int UPSIDE_DOWN_CAKE = 10000; // 0x2710
+ field public static final int UPSIDE_DOWN_CAKE = 34; // 0x22
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index eaa1dbe950df..ace7d59c9a45 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -410,14 +410,14 @@ package android {
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
- field public static final int visualQueryDetectionService;
+ field public static final int visualQueryDetectionService = 16844410; // 0x101067a
}
public static final class R.bool {
- field public static final int config_enableDefaultNotes;
- field public static final int config_enableDefaultNotesForWorkProfile;
+ field public static final int config_enableDefaultNotes = 17891338; // 0x111000a
+ field public static final int config_enableDefaultNotesForWorkProfile = 17891339; // 0x111000b
field public static final int config_enableQrCodeScannerOnLockScreen = 17891336; // 0x1110008
- field public static final int config_safetyProtectionEnabled;
+ field public static final int config_safetyProtectionEnabled = 17891337; // 0x1110009
field public static final int config_sendPackageName = 17891328; // 0x1110000
field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
@@ -430,7 +430,7 @@ package android {
public static final class R.dimen {
field public static final int config_restrictedIconSize = 17104903; // 0x1050007
- field public static final int config_viewConfigurationHandwritingGestureLineMargin;
+ field public static final int config_viewConfigurationHandwritingGestureLineMargin = 17104906; // 0x105000a
}
public static final class R.drawable {
@@ -452,7 +452,7 @@ package android {
field public static final int config_defaultCallRedirection = 17039397; // 0x1040025
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
- field public static final int config_defaultNotes;
+ field public static final int config_defaultNotes = 17039429; // 0x1040045
field public static final int config_defaultSms = 17039396; // 0x1040024
field public static final int config_devicePolicyManagement = 17039421; // 0x104003d
field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
@@ -468,10 +468,10 @@ package android {
field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
- field public static final int config_systemCallStreaming;
+ field public static final int config_systemCallStreaming = 17039431; // 0x1040047
field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
field public static final int config_systemContacts = 17039403; // 0x104002b
- field public static final int config_systemFinancedDeviceController;
+ field public static final int config_systemFinancedDeviceController = 17039430; // 0x1040046
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
field public static final int config_systemSettingsIntelligence = 17039426; // 0x1040042
@@ -483,7 +483,7 @@ package android {
field public static final int config_systemUi = 17039418; // 0x104003a
field public static final int config_systemUiIntelligence = 17039410; // 0x1040032
field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
- field public static final int config_systemWearHealthService;
+ field public static final int config_systemWearHealthService = 17039428; // 0x1040044
field public static final int config_systemWellbeing = 17039408; // 0x1040030
field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
field public static final int safety_protection_display_text = 17039425; // 0x1040041
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9b5e31ac67be..5999e3c4a175 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -8,8 +8,6 @@ package android {
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
- field public static final String BODY_SENSORS_WRIST_TEMPERATURE = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE";
- field public static final String BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND = "android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
@@ -3429,8 +3427,14 @@ package android.view {
method @NonNull public android.hardware.input.InputDeviceIdentifier getIdentifier();
}
+ public abstract class InputEvent implements android.os.Parcelable {
+ method public abstract int getDisplayId();
+ method public abstract void setDisplayId(int);
+ }
+
public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable {
method public static String actionToString(int);
+ method public final int getDisplayId();
method public final void setDisplayId(int);
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
field public static final int LAST_KEYCODE = 316; // 0x13c
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1dddf064e82a..3312294865d6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1450,9 +1450,8 @@ public class AppOpsManager {
public static final int OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD =
AppProtoEnums.APP_OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD;
- /** @hide Access to wrist temperature sensors. */
- public static final int OP_BODY_SENSORS_WRIST_TEMPERATURE =
- AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
+ // App op deprecated/removed.
+ private static final int OP_DEPRECATED_2 = AppProtoEnums.APP_OP_BODY_SENSORS_WRIST_TEMPERATURE;
/**
* Send an intent to launch instead of posting the notification to the status bar.
@@ -1619,7 +1618,6 @@ public class AppOpsManager {
OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION,
OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
- OPSTR_BODY_SENSORS_WRIST_TEMPERATURE,
OPSTR_USE_FULL_SCREEN_INTENT,
OPSTR_CAMERA_SANDBOXED,
OPSTR_RECORD_AUDIO_SANDBOXED
@@ -2221,11 +2219,10 @@ public class AppOpsManager {
"android:capture_consentless_bugreport_on_userdebug_build";
/**
- * Access to wrist temperature body sensors.
+ * App op deprecated/removed.
* @hide
*/
- public static final String OPSTR_BODY_SENSORS_WRIST_TEMPERATURE =
- "android:body_sensors_wrist_temperature";
+ public static final String OPSTR_DEPRECATED_2 = "android:deprecated_2";
/**
* Send an intent to launch instead of posting the notification to the status bar.
@@ -2343,7 +2340,6 @@ public class AppOpsManager {
OP_READ_MEDIA_VISUAL_USER_SELECTED,
OP_FOREGROUND_SERVICE_SPECIAL_USE,
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
- OP_BODY_SENSORS_WRIST_TEMPERATURE,
OP_USE_FULL_SCREEN_INTENT
};
@@ -2763,11 +2759,8 @@ public class AppOpsManager {
"CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD")
.setPermission(Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD)
.build(),
- new AppOpInfo.Builder(OP_BODY_SENSORS_WRIST_TEMPERATURE,
- OPSTR_BODY_SENSORS_WRIST_TEMPERATURE,
- "BODY_SENSORS_WRIST_TEMPERATURE")
- .setPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE)
- .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_DEPRECATED_2, OPSTR_DEPRECATED_2, "DEPRECATED_2")
+ .setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
new AppOpInfo.Builder(OP_USE_FULL_SCREEN_INTENT, OPSTR_USE_FULL_SCREEN_INTENT,
"USE_FULL_SCREEN_INTENT").setPermission(Manifest.permission.USE_FULL_SCREEN_INTENT)
.build(),
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index be012cf8e202..c0c59a24dd8d 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -473,7 +473,6 @@ public abstract class ForegroundServiceTypePolicy {
new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION),
new RegularPermission(Manifest.permission.BODY_SENSORS),
- new RegularPermission(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE),
new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS),
}, false),
FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */,
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index ee242635bfb2..2b1558937d21 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -220,6 +220,20 @@ interface IWallpaperManager {
void notifyGoingToSleep(int x, int y, in Bundle extras);
/**
+ * Called when the screen has been fully turned on and is visible.
+ *
+ * @hide
+ */
+ void notifyScreenTurnedOn(int displayId);
+
+ /**
+ * Called when the screen starts turning on.
+ *
+ * @hide
+ */
+ void notifyScreenTurningOn(int displayId);
+
+ /**
* Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
* dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
*
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index c131ce574d2c..e31486f18dbf 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -2354,8 +2354,7 @@ public class Instrumentation {
return mUiAutomation;
}
if (mustCreateNewAutomation) {
- mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(),
- mUiAutomationConnection);
+ mUiAutomation = new UiAutomation(getTargetContext(), mUiAutomationConnection);
} else {
mUiAutomation.disconnect();
}
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 1df860258d82..bc5f7f411af5 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -110,6 +110,9 @@
"options": [
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceStressTest"
}
],
"file_patterns": ["(/|^)VoiceInteract[^/]*"]
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 658e08444006..247d5bc77ffb 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityService.Callbacks;
@@ -30,6 +32,7 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
@@ -45,6 +48,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Log;
@@ -69,8 +73,10 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.inputmethod.EditorInfo;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import libcore.io.IoUtils;
@@ -202,6 +208,8 @@ public final class UiAutomation {
private final IUiAutomationConnection mUiAutomationConnection;
+ private final int mDisplayId;
+
private HandlerThread mRemoteCallbackThread;
private IAccessibilityServiceClient mClient;
@@ -261,24 +269,49 @@ public final class UiAutomation {
/**
* Creates a new instance that will handle callbacks from the accessibility
+ * layer on the thread of the provided context main looper and perform requests for privileged
+ * operations on the provided connection, and filtering display-related features to the display
+ * associated with the context (or the user running the test, on devices that
+ * {@link UserManager#isVisibleBackgroundUsersSupported() support visible background users}).
+ *
+ * @param context the context associated with the automation
+ * @param connection The connection for performing privileged operations.
+ *
+ * @hide
+ */
+ public UiAutomation(Context context, IUiAutomationConnection connection) {
+ this(getDisplayId(context), context.getMainLooper(), connection);
+ }
+
+ /**
+ * Creates a new instance that will handle callbacks from the accessibility
* layer on the thread of the provided looper and perform requests for privileged
* operations on the provided connection.
*
* @param looper The looper on which to execute accessibility callbacks.
* @param connection The connection for performing privileged operations.
*
+ * @deprecated use {@link #UiAutomation(Context, IUiAutomationConnection)} instead
+ *
* @hide
*/
+ @Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public UiAutomation(Looper looper, IUiAutomationConnection connection) {
- if (looper == null) {
- throw new IllegalArgumentException("Looper cannot be null!");
- }
- if (connection == null) {
- throw new IllegalArgumentException("Connection cannot be null!");
- }
+ this(DEFAULT_DISPLAY, looper, connection);
+ Log.w(LOG_TAG, "Created with deprecatead constructor, assumes DEFAULT_DISPLAY");
+ }
+
+ private UiAutomation(int displayId, Looper looper, IUiAutomationConnection connection) {
+ Preconditions.checkArgument(looper != null, "Looper cannot be null!");
+ Preconditions.checkArgument(connection != null, "Connection cannot be null!");
+
mLocalCallbackHandler = new Handler(looper);
mUiAutomationConnection = connection;
+ mDisplayId = displayId;
+
+ Log.i(LOG_TAG, "Initialized for user " + Process.myUserHandle().getIdentifier()
+ + " on display " + mDisplayId);
}
/**
@@ -719,8 +752,14 @@ public final class UiAutomation {
}
/**
- * Gets the windows on the screen of the default display. This method returns only the windows
- * that a sighted user can interact with, as opposed to all windows.
+ * Gets the windows on the screen associated with the {@link UiAutomation} context (usually the
+ * {@link android.view.Display#DEFAULT_DISPLAY default display).
+ *
+ * <p>
+ * This method returns only the windows that a sighted user can interact with, as opposed to
+ * all windows.
+
+ * <p>
* For example, if there is a modal dialog shown and the user cannot touch
* anything behind it, then only the modal window will be reported
* (assuming it is the top one). For convenience the returned windows
@@ -730,21 +769,23 @@ public final class UiAutomation {
* <strong>Note:</strong> In order to access the windows you have to opt-in
* to retrieve the interactive windows by setting the
* {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
- * </p>
*
* @return The windows if there are windows such, otherwise an empty list.
* @throws IllegalStateException If the connection to the accessibility subsystem is not
* established.
*/
public List<AccessibilityWindowInfo> getWindows() {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "getWindows(): returning windows for display " + mDisplayId);
+ }
final int connectionId;
synchronized (mLock) {
throwIfNotConnectedLocked();
connectionId = mConnectionId;
}
// Calling out without a lock held.
- return AccessibilityInteractionClient.getInstance()
- .getWindows(connectionId);
+ return AccessibilityInteractionClient.getInstance().getWindowsOnDisplay(connectionId,
+ mDisplayId);
}
/**
@@ -1112,8 +1153,10 @@ public final class UiAutomation {
* @return The screenshot bitmap on success, null otherwise.
*/
public Bitmap takeScreenshot() {
- Display display = DisplayManagerGlobal.getInstance()
- .getRealDisplay(Display.DEFAULT_DISPLAY);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Taking screenshot of display " + mDisplayId);
+ }
+ Display display = DisplayManagerGlobal.getInstance().getRealDisplay(mDisplayId);
Point displaySize = new Point();
display.getRealSize(displaySize);
@@ -1126,10 +1169,12 @@ public final class UiAutomation {
screenShot = mUiAutomationConnection.takeScreenshot(
new Rect(0, 0, displaySize.x, displaySize.y));
if (screenShot == null) {
+ Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
+ + mDisplayId);
return null;
}
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while taking screenshot!", re);
+ Log.e(LOG_TAG, "Error while taking screenshot of display " + mDisplayId, re);
return null;
}
@@ -1509,6 +1554,14 @@ public final class UiAutomation {
return executeShellCommandInternal(command, true /* includeStderr */);
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
private ParcelFileDescriptor[] executeShellCommandInternal(
String command, boolean includeStderr) {
warnIfBetterCommand(command);
@@ -1564,6 +1617,7 @@ public final class UiAutomation {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("UiAutomation@").append(Integer.toHexString(hashCode()));
stringBuilder.append("[id=").append(mConnectionId);
+ stringBuilder.append(", displayId=").append(mDisplayId);
stringBuilder.append(", flags=").append(mFlags);
stringBuilder.append("]");
return stringBuilder.toString();
@@ -1601,6 +1655,55 @@ public final class UiAutomation {
return (mFlags & UiAutomation.FLAG_DONT_USE_ACCESSIBILITY) == 0;
}
+ /**
+ * Gets the display id associated with the UiAutomation context.
+ *
+ * <p><b>NOTE: </b> must be a static method because it's called from a constructor to call
+ * another one.
+ */
+ private static int getDisplayId(Context context) {
+ Preconditions.checkArgument(context != null, "Context cannot be null!");
+
+ UserManager userManager = context.getSystemService(UserManager.class);
+ // TODO(b/255426725): given that this is a temporary solution until a11y supports multiple
+ // users, the display is only set on devices that support that
+ if (!userManager.isVisibleBackgroundUsersSupported()) {
+ return DEFAULT_DISPLAY;
+ }
+
+ int displayId = context.getDisplayId();
+ if (displayId == Display.INVALID_DISPLAY) {
+ // Shouldn't happen, but we better handle it
+ Log.e(LOG_TAG, "UiAutomation created UI context with invalid display id, assuming it's"
+ + " running in the display assigned to the user");
+ return getMainDisplayIdAssignedToUser(context, userManager);
+ }
+
+ if (displayId != DEFAULT_DISPLAY) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "getDisplayId(): returning context's display (" + displayId + ")");
+ }
+ // Context is explicitly setting the display, so we respect that...
+ return displayId;
+ }
+ // ...otherwise, we need to get the display the test's user is running on
+ int userDisplayId = getMainDisplayIdAssignedToUser(context, userManager);
+ if (DEBUG) {
+ Log.d(LOG_TAG, "getDisplayId(): returning user's display (" + userDisplayId + ")");
+ }
+ return userDisplayId;
+ }
+
+ private static int getMainDisplayIdAssignedToUser(Context context, UserManager userManager) {
+ if (!userManager.isUserVisible()) {
+ // Should also not happen, but ...
+ Log.e(LOG_TAG, "User (" + context.getUserId() + ") is not visible, using "
+ + "DEFAULT_DISPLAY");
+ return DEFAULT_DISPLAY;
+ }
+ return userManager.getMainDisplayIdAssignedToUser();
+ }
+
private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
public IAccessibilityServiceClientImpl(Looper looper, int generationId) {
@@ -1621,6 +1724,7 @@ public final class UiAutomation {
if (DEBUG) {
Log.d(LOG_TAG, "init(): connectionId=" + connectionId + ", windowToken="
+ windowToken + ", user=" + Process.myUserHandle()
+ + ", UiAutomation.mDisplay=" + UiAutomation.this.mDisplayId
+ ", mGenerationId=" + mGenerationId
+ ", UiAutomation.mGenerationId="
+ UiAutomation.this.mGenerationId);
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 13e800e38cca..d96a9d104ec2 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -22,6 +22,7 @@ import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Bitmap;
@@ -117,7 +118,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
throw new IllegalStateException("Already connected.");
}
mOwningUid = Binder.getCallingUid();
- registerUiTestAutomationServiceLocked(client, flags);
+ registerUiTestAutomationServiceLocked(client,
+ Binder.getCallingUserHandle().getIdentifier(), flags);
storeRotationStateLocked();
}
}
@@ -553,7 +555,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client,
- int flags) {
+ @UserIdInt int userId, int flags) {
IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
@@ -571,10 +573,11 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
- manager.registerUiTestAutomationService(mToken, client, info, flags);
+ manager.registerUiTestAutomationService(mToken, client, info, userId, flags);
mClient = client;
} catch (RemoteException re) {
- throw new IllegalStateException("Error while registering UiTestAutomationService.", re);
+ throw new IllegalStateException("Error while registering UiTestAutomationService for "
+ + "user " + userId + ".", re);
}
}
diff --git a/core/java/android/credentials/ui/CreateCredentialProviderData.java b/core/java/android/credentials/ui/CreateCredentialProviderData.java
index 852934a808e2..629d578c7358 100644
--- a/core/java/android/credentials/ui/CreateCredentialProviderData.java
+++ b/core/java/android/credentials/ui/CreateCredentialProviderData.java
@@ -19,6 +19,7 @@ package android.credentials.ui;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,7 +36,7 @@ import java.util.List;
@TestApi
public final class CreateCredentialProviderData extends ProviderData implements Parcelable {
@NonNull
- private final List<Entry> mSaveEntries;
+ private final ParceledListSlice<Entry> mSaveEntries;
@Nullable
private final Entry mRemoteEntry;
@@ -43,13 +44,13 @@ public final class CreateCredentialProviderData extends ProviderData implements
@NonNull String providerFlattenedComponentName, @NonNull List<Entry> saveEntries,
@Nullable Entry remoteEntry) {
super(providerFlattenedComponentName);
- mSaveEntries = saveEntries;
+ mSaveEntries = new ParceledListSlice<>(saveEntries);
mRemoteEntry = remoteEntry;
}
@NonNull
public List<Entry> getSaveEntries() {
- return mSaveEntries;
+ return mSaveEntries.getList();
}
@Nullable
@@ -60,9 +61,7 @@ public final class CreateCredentialProviderData extends ProviderData implements
private CreateCredentialProviderData(@NonNull Parcel in) {
super(in);
- List<Entry> credentialEntries = new ArrayList<>();
- in.readTypedList(credentialEntries, Entry.CREATOR);
- mSaveEntries = credentialEntries;
+ mSaveEntries = in.readParcelable(null, android.content.pm.ParceledListSlice.class);
AnnotationValidations.validate(NonNull.class, null, mSaveEntries);
Entry remoteEntry = in.readTypedObject(Entry.CREATOR);
@@ -72,7 +71,7 @@ public final class CreateCredentialProviderData extends ProviderData implements
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeTypedList(mSaveEntries);
+ dest.writeParcelable(mSaveEntries, flags);
dest.writeTypedObject(mRemoteEntry, flags);
}
diff --git a/core/java/android/credentials/ui/GetCredentialProviderData.java b/core/java/android/credentials/ui/GetCredentialProviderData.java
index e4688a84a3fb..773dee97f7fe 100644
--- a/core/java/android/credentials/ui/GetCredentialProviderData.java
+++ b/core/java/android/credentials/ui/GetCredentialProviderData.java
@@ -19,6 +19,7 @@ package android.credentials.ui;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,11 +36,11 @@ import java.util.List;
@TestApi
public final class GetCredentialProviderData extends ProviderData implements Parcelable {
@NonNull
- private final List<Entry> mCredentialEntries;
+ private final ParceledListSlice<Entry> mCredentialEntries;
@NonNull
- private final List<Entry> mActionChips;
+ private final ParceledListSlice<Entry> mActionChips;
@NonNull
- private final List<AuthenticationEntry> mAuthenticationEntries;
+ private final ParceledListSlice<AuthenticationEntry> mAuthenticationEntries;
@Nullable
private final Entry mRemoteEntry;
@@ -49,25 +50,25 @@ public final class GetCredentialProviderData extends ProviderData implements Par
@NonNull List<AuthenticationEntry> authenticationEntries,
@Nullable Entry remoteEntry) {
super(providerFlattenedComponentName);
- mCredentialEntries = credentialEntries;
- mActionChips = actionChips;
- mAuthenticationEntries = authenticationEntries;
+ mCredentialEntries = new ParceledListSlice<>(credentialEntries);
+ mActionChips = new ParceledListSlice<>(actionChips);
+ mAuthenticationEntries = new ParceledListSlice<>(authenticationEntries);
mRemoteEntry = remoteEntry;
}
@NonNull
public List<Entry> getCredentialEntries() {
- return mCredentialEntries;
+ return mCredentialEntries.getList();
}
@NonNull
public List<Entry> getActionChips() {
- return mActionChips;
+ return mActionChips.getList();
}
@NonNull
public List<AuthenticationEntry> getAuthenticationEntries() {
- return mAuthenticationEntries;
+ return mAuthenticationEntries.getList();
}
@Nullable
@@ -77,20 +78,16 @@ public final class GetCredentialProviderData extends ProviderData implements Par
private GetCredentialProviderData(@NonNull Parcel in) {
super(in);
-
- List<Entry> credentialEntries = new ArrayList<>();
- in.readTypedList(credentialEntries, Entry.CREATOR);
- mCredentialEntries = credentialEntries;
+ mCredentialEntries = in.readParcelable(null,
+ android.content.pm.ParceledListSlice.class);
AnnotationValidations.validate(NonNull.class, null, mCredentialEntries);
- List<Entry> actionChips = new ArrayList<>();
- in.readTypedList(actionChips, Entry.CREATOR);
- mActionChips = actionChips;
+ mActionChips = in.readParcelable(null,
+ android.content.pm.ParceledListSlice.class);
AnnotationValidations.validate(NonNull.class, null, mActionChips);
- List<AuthenticationEntry> authenticationEntries = new ArrayList<>();
- in.readTypedList(authenticationEntries, AuthenticationEntry.CREATOR);
- mAuthenticationEntries = authenticationEntries;
+ mAuthenticationEntries = in.readParcelable(null,
+ android.content.pm.ParceledListSlice.class);
AnnotationValidations.validate(NonNull.class, null, mAuthenticationEntries);
Entry remoteEntry = in.readTypedObject(Entry.CREATOR);
@@ -100,9 +97,9 @@ public final class GetCredentialProviderData extends ProviderData implements Par
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeTypedList(mCredentialEntries);
- dest.writeTypedList(mActionChips);
- dest.writeTypedList(mAuthenticationEntries);
+ dest.writeParcelable(mCredentialEntries, flags);
+ dest.writeParcelable(mActionChips, flags);
+ dest.writeParcelable(mAuthenticationEntries, flags);
dest.writeTypedObject(mRemoteEntry, flags);
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index fa678fc5ee1a..2e40f6096ccb 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -142,6 +142,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
private PromptInfo mPromptInfo;
private ButtonInfo mNegativeButtonInfo;
private Context mContext;
+ private IAuthService mService;
/**
* Creates a builder for a {@link BiometricPrompt} dialog.
@@ -212,6 +213,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
+ * @param service
+ * @return This builder.
+ * @hide
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ @NonNull
+ public Builder setService(@NonNull IAuthService service) {
+ mService = service;
+ return this;
+ }
+
+ /**
* Sets an optional title, subtitle, and/or description that will override other text when
* the user is authenticating with PIN/pattern/password. Currently for internal use only.
* @return This builder.
@@ -472,7 +485,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
throw new IllegalArgumentException("Can't have both negative button behavior"
+ " and device credential enabled");
}
- return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo);
+ mService = (mService == null) ? IAuthService.Stub.asInterface(
+ ServiceManager.getService(Context.AUTH_SERVICE)) : mService;
+ return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, mService);
}
}
@@ -521,7 +536,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
public void onAuthenticationFailed() {
mExecutor.execute(() -> {
mAuthenticationCallback.onAuthenticationFailed();
- mIsPromptShowing = false;
});
}
@@ -604,12 +618,12 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
private boolean mIsPromptShowing;
- private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo) {
+ private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo,
+ IAuthService service) {
mContext = context;
mPromptInfo = promptInfo;
mNegativeButtonInfo = negativeButtonInfo;
- mService = IAuthService.Stub.asInterface(
- ServiceManager.getService(Context.AUTH_SERVICE));
+ mService = service;
mIsPromptShowing = false;
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 5feda785ece3..ad68866571e3 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -1310,6 +1310,10 @@ public abstract class CameraDevice implements AutoCloseable {
* {@link Surface}, submitting a reprocess {@link CaptureRequest} with multiple
* output targets will result in a {@link CaptureFailure}.
*
+ * From Android 14 onward, {@link CaptureRequest#CONTROL_CAPTURE_INTENT} will be set to
+ * {@link CameraMetadata#CONTROL_CAPTURE_INTENT_STILL_CAPTURE} by default. Prior to Android 14,
+ * apps will need to explicitly set this key themselves.
+ *
* @param inputResult The capture result of the output image or one of the output images used
* to generate the reprocess input image for this capture request.
*
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index cb1efe8c2b55..f2d8caaab0e7 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -26,6 +26,7 @@ import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
@@ -861,8 +862,13 @@ public class CameraDeviceImpl extends CameraDevice
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
- return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
- inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
+ CaptureRequest.Builder builder = new CaptureRequest.Builder(resultMetadata,
+ /*reprocess*/true, inputResult.getSessionId(), getId(),
+ /*physicalCameraIdSet*/ null);
+ builder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CameraMetadata.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
+
+ return builder;
}
}
diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
index dcc336966810..466373030c78 100644
--- a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
+++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
@@ -42,12 +42,6 @@ oneway interface IRecognitionStatusCallback {
void onGenericSoundTriggerDetected(in SoundTrigger.GenericRecognitionEvent recognitionEvent);
/**
- * Called when the detection fails due to an error.
- *
- * @param status The error code that was seen.
- */
- void onError(int status);
- /**
* Called when the recognition is paused temporarily for some reason.
*/
void onRecognitionPaused();
@@ -55,4 +49,28 @@ oneway interface IRecognitionStatusCallback {
* Called when the recognition is resumed after it was temporarily paused.
*/
void onRecognitionResumed();
+
+ // Error callbacks to follow
+ /**
+ * Called when this recognition has been preempted by another.
+ */
+ void onPreempted();
+
+ /**
+ * Called when the underlying ST module service has died.
+ */
+ void onModuleDied();
+
+ /**
+ * Called when the service failed to gracefully resume recognition following a pause.
+ * @param status - The received error code.
+ */
+ void onResumeFailed(int status);
+
+ /**
+ * Called when the service failed to pause recognition when required.
+ * TODO(b/276507281) Remove. This should never happen, so we should abort instead.
+ * @param status - The received error code.
+ */
+ void onPauseFailed(int status);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 244632a87593..7383e633fb93 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -493,7 +493,7 @@ public class Build {
* @hide
*/
@TestApi
- public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
+ public static final int RESOURCES_SDK_INT = SDK_INT;
/**
* The current lowest supported value of app target SDK. Applications targeting
@@ -1222,7 +1222,7 @@ public class Build {
/**
* Upside Down Cake.
*/
- public static final int UPSIDE_DOWN_CAKE = CUR_DEVELOPMENT;
+ public static final int UPSIDE_DOWN_CAKE = 34;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d2f9ff01ca98..59b945c9c9a4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -2051,6 +2051,14 @@ public final class Telephony {
* <P>Type: TEXT</P>
*/
public static final String ADDRESS = "address";
+
+ /**
+ * The subscription to which the message belongs to. Its value will be less than 0
+ * if the sub id cannot be determined.
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUBSCRIPTION_ID = "sub_id";
}
/**
@@ -2119,6 +2127,14 @@ public final class Telephony {
* <P>Type: INTEGER (boolean)</P>
*/
public static final String ARCHIVED = "archived";
+
+ /**
+ * The subscription to which the message belongs to. Its value will be less than 0
+ * if the sub id cannot be determined.
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUBSCRIPTION_ID = "sub_id";
}
/**
@@ -2477,6 +2493,14 @@ public final class Telephony {
public static final String CHARSET = "charset";
/**
+ * The subscription to which the message belongs to. Its value will be less than 0
+ * if the sub id cannot be determined.
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUBSCRIPTION_ID = "sub_id";
+
+ /**
* Generates a Addr {@link Uri} for message, used to perform Addr table operation
* for mms.
*
@@ -2597,6 +2621,14 @@ public final class Telephony {
public static final String TEXT = "text";
/**
+ * The subscription to which the message belongs to. Its value will be less than 0
+ * if the sub id cannot be determined.
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUBSCRIPTION_ID = "sub_id";
+
+ /**
* Generates a Part {@link Uri} for message, used to perform Part table operation
* for mms.
*
@@ -2635,6 +2667,14 @@ public final class Telephony {
* <P>Type: INTEGER (long)</P>
*/
public static final String SENT_TIME = "sent_time";
+
+ /**
+ * The subscription to which the message belongs to. Its value will be less than 0
+ * if the sub id cannot be determined.
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUBSCRIPTION_ID = "sub_id";
}
/**
@@ -2868,6 +2908,14 @@ public final class Telephony {
* <P>Type: TEXT</P>
*/
public static final String INDEXED_TEXT = "index_text";
+
+ /**
+ * The subscription to which the message belongs to. Its value will be less than 0
+ * if the sub id cannot be determined.
+ * <p>Type: INTEGER (long) </p>
+ * @hide
+ */
+ public static final String SUBSCRIPTION_ID = "sub_id";
}
}
diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
index cd53cb6afc71..df934335e49d 100644
--- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +34,7 @@ import java.util.Objects;
* Response to a {@link BeginCreateCredentialRequest}.
*/
public final class BeginCreateCredentialResponse implements Parcelable {
- private final @NonNull List<CreateEntry> mCreateEntries;
+ private final @NonNull ParceledListSlice<CreateEntry> mCreateEntries;
private final @Nullable RemoteEntry mRemoteCreateEntry;
/**
@@ -41,19 +42,19 @@ public final class BeginCreateCredentialResponse implements Parcelable {
* to return.
*/
public BeginCreateCredentialResponse() {
- this(/*createEntries=*/new ArrayList<>(), /*remoteCreateEntry=*/null);
+ this(/*createEntries=*/new ParceledListSlice<>(new ArrayList<>()),
+ /*remoteCreateEntry=*/null);
}
private BeginCreateCredentialResponse(@NonNull Parcel in) {
- List<CreateEntry> createEntries = new ArrayList<>();
- in.readTypedList(createEntries, CreateEntry.CREATOR);
- mCreateEntries = createEntries;
+ mCreateEntries = in.readParcelable(
+ null, android.content.pm.ParceledListSlice.class);
mRemoteCreateEntry = in.readTypedObject(RemoteEntry.CREATOR);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeTypedList(mCreateEntries);
+ dest.writeParcelable(mCreateEntries, flags);
dest.writeTypedObject(mRemoteCreateEntry, flags);
}
@@ -76,7 +77,7 @@ public final class BeginCreateCredentialResponse implements Parcelable {
};
/* package-private */ BeginCreateCredentialResponse(
- @NonNull List<CreateEntry> createEntries,
+ @NonNull ParceledListSlice<CreateEntry> createEntries,
@Nullable RemoteEntry remoteCreateEntry) {
this.mCreateEntries = createEntries;
com.android.internal.util.AnnotationValidations.validate(
@@ -86,7 +87,7 @@ public final class BeginCreateCredentialResponse implements Parcelable {
/** Returns the list of create entries to be displayed on the UI. */
public @NonNull List<CreateEntry> getCreateEntries() {
- return mCreateEntries;
+ return mCreateEntries.getList();
}
/** Returns the remote create entry to be displayed on the UI. */
@@ -159,7 +160,9 @@ public final class BeginCreateCredentialResponse implements Parcelable {
* Builds a new instance of {@link BeginCreateCredentialResponse}.
*/
public @NonNull BeginCreateCredentialResponse build() {
- return new BeginCreateCredentialResponse(mCreateEntries, mRemoteCreateEntry);
+ return new BeginCreateCredentialResponse(
+ new ParceledListSlice<>(mCreateEntries),
+ mRemoteCreateEntry);
}
}
}
diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java
index e25b6869605d..5ed06ac1ade7 100644
--- a/core/java/android/service/credentials/BeginGetCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,13 +36,13 @@ import java.util.Objects;
*/
public final class BeginGetCredentialResponse implements Parcelable {
/** List of credential entries to be displayed on the UI. */
- private final @NonNull List<CredentialEntry> mCredentialEntries;
+ private final @NonNull ParceledListSlice<CredentialEntry> mCredentialEntries;
/** List of authentication entries to be displayed on the UI. */
- private final @NonNull List<Action> mAuthenticationEntries;
+ private final @NonNull ParceledListSlice<Action> mAuthenticationEntries;
/** List of provider actions to be displayed on the UI. */
- private final @NonNull List<Action> mActions;
+ private final @NonNull ParceledListSlice<Action> mActions;
/** Remote credential entry to get the response from a different device. */
private final @Nullable RemoteEntry mRemoteCredentialEntry;
@@ -51,31 +52,30 @@ public final class BeginGetCredentialResponse implements Parcelable {
* or {@link Action} to return.
*/
public BeginGetCredentialResponse() {
- this(/*credentialEntries=*/new ArrayList<>(),
- /*authenticationActions=*/new ArrayList<>(),
- /*actions=*/new ArrayList<>(),
+ this(/*credentialEntries=*/new ParceledListSlice<>(new ArrayList<>()),
+ /*authenticationEntries=*/new ParceledListSlice<>(new ArrayList<>()),
+ /*actions=*/new ParceledListSlice<>(new ArrayList<>()),
/*remoteCredentialEntry=*/null);
}
- private BeginGetCredentialResponse(@NonNull List<CredentialEntry> credentialEntries,
- @NonNull List<Action> authenticationEntries, @NonNull List<Action> actions,
+ private BeginGetCredentialResponse(
+ @NonNull ParceledListSlice<CredentialEntry> credentialEntries,
+ @NonNull ParceledListSlice<Action> authenticationEntries,
+ @NonNull ParceledListSlice<Action> actions,
@Nullable RemoteEntry remoteCredentialEntry) {
- mCredentialEntries = new ArrayList<>(credentialEntries);
- mAuthenticationEntries = new ArrayList<>(authenticationEntries);
- mActions = new ArrayList<>(actions);
+ mCredentialEntries = credentialEntries;
+ mAuthenticationEntries = authenticationEntries;
+ mActions = actions;
mRemoteCredentialEntry = remoteCredentialEntry;
}
private BeginGetCredentialResponse(@NonNull Parcel in) {
- List<CredentialEntry> credentialEntries = new ArrayList<>();
- in.readTypedList(credentialEntries, CredentialEntry.CREATOR);
- mCredentialEntries = credentialEntries;
- List<Action> authenticationEntries = new ArrayList<>();
- in.readTypedList(authenticationEntries, Action.CREATOR);
- mAuthenticationEntries = authenticationEntries;
- List<Action> actions = new ArrayList<>();
- in.readTypedList(actions, Action.CREATOR);
- mActions = actions;
+ mCredentialEntries = in.readParcelable(
+ null, android.content.pm.ParceledListSlice.class);
+ mAuthenticationEntries = in.readParcelable(
+ null, android.content.pm.ParceledListSlice.class);
+ mActions = in.readParcelable(
+ null, android.content.pm.ParceledListSlice.class);
mRemoteCredentialEntry = in.readTypedObject(RemoteEntry.CREATOR);
}
@@ -99,9 +99,9 @@ public final class BeginGetCredentialResponse implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeTypedList(mCredentialEntries, flags);
- dest.writeTypedList(mAuthenticationEntries, flags);
- dest.writeTypedList(mActions, flags);
+ dest.writeParcelable(mCredentialEntries, flags);
+ dest.writeParcelable(mAuthenticationEntries, flags);
+ dest.writeParcelable(mActions, flags);
dest.writeTypedObject(mRemoteCredentialEntry, flags);
}
@@ -109,21 +109,22 @@ public final class BeginGetCredentialResponse implements Parcelable {
* Returns the list of credential entries to be displayed on the UI.
*/
public @NonNull List<CredentialEntry> getCredentialEntries() {
- return mCredentialEntries;
+ return mCredentialEntries.getList();
}
/**
* Returns the list of authentication entries to be displayed on the UI.
*/
public @NonNull List<Action> getAuthenticationActions() {
- return mAuthenticationEntries;
+ return mAuthenticationEntries.getList();
}
/**
* Returns the list of actions to be displayed on the UI.
*/
public @NonNull List<Action> getActions() {
- return mActions;
+
+ return mActions.getList();
}
/**
@@ -268,8 +269,11 @@ public final class BeginGetCredentialResponse implements Parcelable {
* Builds a {@link BeginGetCredentialResponse} instance.
*/
public @NonNull BeginGetCredentialResponse build() {
- return new BeginGetCredentialResponse(mCredentialEntries, mAuthenticationEntries,
- mActions, mRemoteCredentialEntry);
+ return new BeginGetCredentialResponse(
+ new ParceledListSlice<>(mCredentialEntries),
+ new ParceledListSlice<>(mAuthenticationEntries),
+ new ParceledListSlice<>(mActions),
+ mRemoteCredentialEntry);
}
}
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 8688a18880b7..24c96eae03cb 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -1573,16 +1573,6 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
@Override
- public void onError(int status) {
- Slog.i(TAG, "onError: " + status);
- // TODO(b/271534248): This is a workaround before the sound trigger uses the new error
- // method.
- Message.obtain(mHandler, MSG_DETECTION_SOUND_TRIGGER_FAILURE,
- new SoundTriggerFailure(SoundTriggerFailure.ERROR_CODE_UNKNOWN,
- "Sound trigger error")).sendToTarget();
- }
-
- @Override
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
Slog.v(TAG, "onHotwordDetectionServiceFailure: " + hotwordDetectionServiceFailure);
@@ -1605,6 +1595,12 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
@Override
+ public void onSoundTriggerFailure(SoundTriggerFailure soundTriggerFailure) {
+ Message.obtain(mHandler, MSG_DETECTION_SOUND_TRIGGER_FAILURE,
+ Objects.requireNonNull(soundTriggerFailure)).sendToTarget();
+ }
+
+ @Override
public void onUnknownFailure(String errorMessage) throws RemoteException {
Slog.v(TAG, "onUnknownFailure: " + errorMessage);
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE,
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index eac7aee43859..7ab4fafcf312 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -234,14 +234,6 @@ class SoftwareHotwordDetector extends AbstractDetector {
}
@Override
- public void onError(int status) throws RemoteException {
- if (DEBUG) {
- Slog.i(TAG, "Ignored #onError (" + status + ") event");
- }
- // TODO: Check if we still need to implement this method with DetectorFailure mechanism.
- }
-
- @Override
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure)
throws RemoteException {
@@ -265,6 +257,13 @@ class SoftwareHotwordDetector extends AbstractDetector {
}
@Override
+ public void onSoundTriggerFailure(SoundTriggerFailure onSoundTriggerFailure)
+ throws RemoteException {
+ // It should never be called here.
+ Slog.wtf(TAG, "Unexpected STFailure in software detector: " + onSoundTriggerFailure);
+ }
+
+ @Override
public void onUnknownFailure(String errorMessage) throws RemoteException {
Slog.v(TAG, "onUnknownFailure: " + errorMessage);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
diff --git a/core/java/android/service/voice/SoundTriggerFailure.java b/core/java/android/service/voice/SoundTriggerFailure.java
index 5560800a373f..2ce5e5da4724 100644
--- a/core/java/android/service/voice/SoundTriggerFailure.java
+++ b/core/java/android/service/voice/SoundTriggerFailure.java
@@ -73,18 +73,28 @@ public final class SoundTriggerFailure implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface SoundTriggerErrorCode {}
- private int mErrorCode = ERROR_CODE_UNKNOWN;
- private String mErrorMessage = "Unknown";
+ private final int mErrorCode;
+ private final String mErrorMessage;
/**
* @hide
*/
@TestApi
- public SoundTriggerFailure(int errorCode, @NonNull String errorMessage) {
+ public SoundTriggerFailure(@SoundTriggerErrorCode int errorCode,
+ @NonNull String errorMessage) {
if (TextUtils.isEmpty(errorMessage)) {
throw new IllegalArgumentException("errorMessage is empty or null.");
}
- mErrorCode = errorCode;
+ switch (errorCode) {
+ case ERROR_CODE_UNKNOWN:
+ case ERROR_CODE_MODULE_DIED:
+ case ERROR_CODE_RECOGNITION_RESUME_FAILED:
+ case ERROR_CODE_UNEXPECTED_PREEMPTION:
+ mErrorCode = errorCode;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid ErrorCode: " + errorCode);
+ }
mErrorMessage = errorMessage;
}
@@ -110,13 +120,14 @@ public final class SoundTriggerFailure implements Parcelable {
@FailureSuggestedAction.FailureSuggestedActionDef
public int getSuggestedAction() {
switch (mErrorCode) {
+ case ERROR_CODE_UNKNOWN:
case ERROR_CODE_MODULE_DIED:
case ERROR_CODE_UNEXPECTED_PREEMPTION:
return FailureSuggestedAction.RECREATE_DETECTOR;
case ERROR_CODE_RECOGNITION_RESUME_FAILED:
return FailureSuggestedAction.RESTART_RECOGNITION;
default:
- return FailureSuggestedAction.NONE;
+ throw new AssertionError("Unexpected error code");
}
}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index b4f5ff1046ae..93b7964705ba 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -391,12 +391,6 @@ public class VisualQueryDetector {
}
@Override
- public void onError(int status) throws RemoteException {
- Slog.v(TAG, "Initialization Error: (" + status + ")");
- // Do nothing
- }
-
- @Override
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure)
throws RemoteException {
@@ -420,6 +414,11 @@ public class VisualQueryDetector {
}
@Override
+ public void onSoundTriggerFailure(SoundTriggerFailure soundTriggerFailure) {
+ Slog.wtf(TAG, "Unexpected STFailure in VisualQueryDetector" + soundTriggerFailure);
+ }
+
+ @Override
public void onUnknownFailure(String errorMessage) throws RemoteException {
Slog.v(TAG, "onUnknownFailure: " + errorMessage);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index 4c51be0ab8d9..f1ae22eca873 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -32,6 +32,8 @@ interface IWallpaperEngine {
oneway void setDisplayPadding(in Rect padding);
@UnsupportedAppUsage
oneway void setVisibility(boolean visible);
+ oneway void onScreenTurningOn();
+ oneway void onScreenTurnedOn();
oneway void setInAmbientMode(boolean inAmbientDisplay, long animationDuration);
@UnsupportedAppUsage
oneway void dispatchPointer(in MotionEvent event);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0b947fc18237..77bbeb59927a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -168,6 +168,7 @@ public abstract class WallpaperService extends Service {
private static final int MSG_ZOOM = 10100;
private static final int MSG_RESIZE_PREVIEW = 10110;
private static final int MSG_REPORT_SHOWN = 10150;
+ private static final int MSG_UPDATE_SCREEN_TURNING_ON = 10170;
private static final int MSG_UPDATE_DIMMING = 10200;
private static final int MSG_WALLPAPER_FLAGS_CHANGED = 10210;
@@ -213,6 +214,16 @@ public abstract class WallpaperService extends Service {
boolean mInitializing = true;
boolean mVisible;
+ /**
+ * Whether the screen is turning on.
+ * After the display is powered on, brightness is initially off. It is turned on only after
+ * all windows have been drawn, and sysui notifies that it's ready (See
+ * {@link com.android.internal.policy.IKeyguardDrawnCallback}).
+ * As some wallpapers use visibility as a signal to start animations, this makes sure
+ * {@link Engine#onVisibilityChanged} is invoked only when the display is both on and
+ * visible (with brightness on).
+ */
+ private boolean mIsScreenTurningOn;
boolean mReportedVisible;
boolean mDestroyed;
// Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false
@@ -1018,6 +1029,7 @@ public abstract class WallpaperService extends Service {
out.print(" mDestroyed="); out.println(mDestroyed);
out.print(prefix); out.print("mVisible="); out.print(mVisible);
out.print(" mReportedVisible="); out.println(mReportedVisible);
+ out.print(" mIsScreenTurningOn="); out.println(mIsScreenTurningOn);
out.print(prefix); out.print("mDisplay="); out.println(mDisplay);
out.print(prefix); out.print("mCreated="); out.print(mCreated);
out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
@@ -1549,6 +1561,13 @@ public abstract class WallpaperService extends Service {
}
}
+ void onScreenTurningOnChanged(boolean isScreenTurningOn) {
+ if (!mDestroyed) {
+ mIsScreenTurningOn = isScreenTurningOn;
+ reportVisibility(false);
+ }
+ }
+
void doVisibilityChanged(boolean visible) {
if (!mDestroyed) {
mVisible = visible;
@@ -1565,9 +1584,10 @@ public abstract class WallpaperService extends Service {
return;
}
if (!mDestroyed) {
- mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN :
- mDisplay.getCommittedState();
- boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
+ mDisplayState =
+ mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getCommittedState();
+ boolean displayVisible = Display.isOnState(mDisplayState) && !mIsScreenTurningOn;
+ boolean visible = mVisible && displayVisible;
if (DEBUG) {
Log.v(
TAG,
@@ -2486,6 +2506,20 @@ public abstract class WallpaperService extends Service {
}
}
+ public void updateScreenTurningOn(boolean isScreenTurningOn) {
+ Message msg = mCaller.obtainMessageBO(MSG_UPDATE_SCREEN_TURNING_ON, isScreenTurningOn,
+ null);
+ mCaller.sendMessage(msg);
+ }
+
+ public void onScreenTurningOn() throws RemoteException {
+ updateScreenTurningOn(true);
+ }
+
+ public void onScreenTurnedOn() throws RemoteException {
+ updateScreenTurningOn(false);
+ }
+
@Override
public void executeMessage(Message message) {
switch (message.what) {
@@ -2530,6 +2564,13 @@ public abstract class WallpaperService extends Service {
+ ": " + message.arg1);
mEngine.doVisibilityChanged(message.arg1 != 0);
break;
+ case MSG_UPDATE_SCREEN_TURNING_ON:
+ if (DEBUG) {
+ Log.v(TAG,
+ message.arg1 != 0 ? "Screen turning on" : "Screen turned on");
+ }
+ mEngine.onScreenTurningOnChanged(/* isScreenTurningOn= */ message.arg1 != 0);
+ break;
case MSG_WALLPAPER_OFFSETS: {
mEngine.doOffsetsChanged(true);
} break;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index c3295088ef11..d87198a0dc85 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -128,6 +128,12 @@ public class FeatureFlagUtils {
*/
public static final String SETTINGS_ENABLE_SPA_PHASE2 = "settings_enable_spa_phase2";
+ /**
+ * Enable the SPA metrics writing.
+ * @hide
+ */
+ public static final String SETTINGS_ENABLE_SPA_METRICS = "settings_enable_spa_metrics";
+
/** Flag to enable/disable adb log metrics
* @hide
*/
@@ -226,6 +232,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_PHASE2, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "false");
DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true");
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 9a93e1b9e1f9..d06b0ce1a2d8 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -354,6 +354,15 @@ public class TimeUtils {
}
/** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long time, long now, StringBuilder sb) {
+ if (time == 0) {
+ sb.append("--");
+ return;
+ }
+ formatDuration(time-now, sb, 0);
+ }
+
+ /** @hide Just for debugging; not internationalized. */
public static void formatDuration(long time, long now, PrintWriter pw) {
if (time == 0) {
pw.print("--");
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 8c4e90c81147..5dd2d82200bc 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -785,12 +785,13 @@ public final class Choreographer {
DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
+ boolean resynced = false;
try {
+ FrameTimeline timeline = mFrameData.update(frameTimeNanos, vsyncEventData);
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW,
- "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId);
+ Trace.traceBegin(
+ Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + timeline.mVsyncId);
}
- mFrameData.update(frameTimeNanos, vsyncEventData);
synchronized (mLock) {
if (!mFrameScheduled) {
traceMessage("Frame not scheduled");
@@ -828,7 +829,9 @@ public final class Choreographer {
+ " ms in the past.");
}
}
- mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos);
+ timeline = mFrameData.update(
+ frameTimeNanos, mDisplayEventReceiver, jitterNanos);
+ resynced = true;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
@@ -860,6 +863,12 @@ public final class Choreographer {
mLastVsyncEventData = vsyncEventData;
}
+ if (resynced && Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ String message = String.format("Choreographer#doFrame - resynced to %d in %.1fms",
+ timeline.mVsyncId, (timeline.mDeadlineNanos - startNanos) * 0.000001f);
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, message);
+ }
+
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
@@ -875,6 +884,9 @@ public final class Choreographer {
doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
+ if (resynced) {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
@@ -1149,7 +1161,8 @@ public final class Choreographer {
* Update the frame data with a {@code DisplayEventReceiver.VsyncEventData} received from
* native.
*/
- void update(long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
+ FrameTimeline update(
+ long frameTimeNanos, DisplayEventReceiver.VsyncEventData vsyncEventData) {
if (vsyncEventData.frameTimelines.length != mFrameTimelines.length) {
throw new IllegalStateException(
"Length of native frame timelines received does not match Java. Did "
@@ -1164,6 +1177,7 @@ public final class Choreographer {
mFrameTimelines[i].update(frameTimeline.vsyncId,
frameTimeline.expectedPresentationTime, frameTimeline.deadline);
}
+ return mFrameTimelines[mPreferredFrameTimelineIndex];
}
/**
@@ -1171,7 +1185,7 @@ public final class Choreographer {
*
* @param jitterNanos currentTime - frameTime
*/
- void update(
+ FrameTimeline update(
long frameTimeNanos, DisplayEventReceiver displayEventReceiver, long jitterNanos) {
int newPreferredIndex = 0;
final long minimumDeadline =
@@ -1192,6 +1206,7 @@ public final class Choreographer {
} else {
update(frameTimeNanos, newPreferredIndex);
}
+ return mFrameTimelines[mPreferredFrameTimelineIndex];
}
void update(long frameTimeNanos, int newPreferredFrameTimelineIndex) {
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 0b4adaeb9890..a8e68b71f5cc 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -100,6 +101,7 @@ public abstract class InputEvent implements Parcelable {
* @return The display id associated with the event.
* @hide
*/
+ @TestApi
public abstract int getDisplayId();
/**
@@ -107,6 +109,7 @@ public abstract class InputEvent implements Parcelable {
* @param displayId
* @hide
*/
+ @TestApi
public abstract void setDisplayId(int displayId);
/**
* Copies the event.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index b6d9400fad5c..858da554c670 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -2100,6 +2100,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
}
/** @hide */
+ @TestApi
@Override
public final int getDisplayId() {
return mDisplayId;
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index cd89a561074c..e9984daaf389 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -303,6 +303,7 @@ public class SurfaceControlViewHost {
/** @hide */
public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
@NonNull WindowlessWindowManager wwm, @NonNull String callsite) {
+ mSurfaceControl = wwm.mRootSurface;
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
mCloseGuard.openWithCallSite("release", callsite);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 1fac142df481..390503b80bad 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -62,7 +62,7 @@ interface IAccessibilityManager {
in IAccessibilityInteractionConnection connection);
void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient client,
- in AccessibilityServiceInfo info, int flags);
+ in AccessibilityServiceInfo info, int userId, int flags);
void unregisterUiTestAutomationService(IAccessibilityServiceClient client);
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index 8012a1c26bac..c47572313eeb 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -34,6 +34,8 @@ public final class BackMotionEvent implements Parcelable {
private final float mTouchX;
private final float mTouchY;
private final float mProgress;
+ private final float mVelocityX;
+ private final float mVelocityY;
@BackEvent.SwipeEdge
private final int mSwipeEdge;
@@ -43,19 +45,32 @@ public final class BackMotionEvent implements Parcelable {
/**
* Creates a new {@link BackMotionEvent} instance.
*
+ * <p>Note: Velocity is only computed for last event, for performance reasons.</p>
+ *
* @param touchX Absolute X location of the touch point of this event.
* @param touchY Absolute Y location of the touch point of this event.
* @param progress Value between 0 and 1 on how far along the back gesture is.
+ * @param velocityX X velocity computed from the touch point of this event.
+ * Value in pixels/second. {@link Float#NaN} if was not computed.
+ * @param velocityY Y velocity computed from the touch point of this event.
+ * Value in pixels/second. {@link Float#NaN} if was not computed.
* @param swipeEdge Indicates which edge the swipe starts from.
* @param departingAnimationTarget The remote animation target of the departing
* application window.
*/
- public BackMotionEvent(float touchX, float touchY, float progress,
+ public BackMotionEvent(
+ float touchX,
+ float touchY,
+ float progress,
+ float velocityX,
+ float velocityY,
@BackEvent.SwipeEdge int swipeEdge,
@Nullable RemoteAnimationTarget departingAnimationTarget) {
mTouchX = touchX;
mTouchY = touchY;
mProgress = progress;
+ mVelocityX = velocityX;
+ mVelocityY = velocityY;
mSwipeEdge = swipeEdge;
mDepartingAnimationTarget = departingAnimationTarget;
}
@@ -64,6 +79,8 @@ public final class BackMotionEvent implements Parcelable {
mTouchX = in.readFloat();
mTouchY = in.readFloat();
mProgress = in.readFloat();
+ mVelocityX = in.readFloat();
+ mVelocityY = in.readFloat();
mSwipeEdge = in.readInt();
mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
}
@@ -91,11 +108,27 @@ public final class BackMotionEvent implements Parcelable {
dest.writeFloat(mTouchX);
dest.writeFloat(mTouchY);
dest.writeFloat(mProgress);
+ dest.writeFloat(mVelocityX);
+ dest.writeFloat(mVelocityY);
dest.writeInt(mSwipeEdge);
dest.writeTypedObject(mDepartingAnimationTarget, flags);
}
/**
+ * Returns the absolute X location of the touch point.
+ */
+ public float getTouchX() {
+ return mTouchX;
+ }
+
+ /**
+ * Returns the absolute Y location of the touch point.
+ */
+ public float getTouchY() {
+ return mTouchY;
+ }
+
+ /**
* Returns the progress of a {@link BackEvent}.
*
* @see BackEvent#getProgress()
@@ -106,17 +139,21 @@ public final class BackMotionEvent implements Parcelable {
}
/**
- * Returns the absolute X location of the touch point.
+ * Returns the X velocity computed from the touch point.
+ *
+ * @return value in pixels/second or {@link Float#NaN} if was not computed.
*/
- public float getTouchX() {
- return mTouchX;
+ public float getVelocityX() {
+ return mVelocityX;
}
/**
- * Returns the absolute Y location of the touch point.
+ * Returns the Y velocity computed from the touch point.
+ *
+ * @return value in pixels/second or {@link Float#NaN} if was not computed.
*/
- public float getTouchY() {
- return mTouchY;
+ public float getVelocityY() {
+ return mVelocityY;
}
/**
@@ -143,6 +180,8 @@ public final class BackMotionEvent implements Parcelable {
+ "mTouchX=" + mTouchX
+ ", mTouchY=" + mTouchY
+ ", mProgress=" + mProgress
+ + ", mVelocityX=" + mVelocityX
+ + ", mVelocityY=" + mVelocityY
+ ", mSwipeEdge" + mSwipeEdge
+ ", mDepartingAnimationTarget" + mDepartingAnimationTarget
+ "}";
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index ad0d1a401991..380118846dc7 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -20,6 +20,7 @@ import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionServiceFailure;
import android.service.voice.HotwordRejectedResult;
+import android.service.voice.SoundTriggerFailure;
import android.service.voice.VisualQueryDetectionServiceFailure;
/**
@@ -57,13 +58,6 @@ oneway interface IHotwordRecognitionStatusCallback {
void onRejected(in HotwordRejectedResult result);
/**
- * Called when the detection fails due to an error.
- *
- * @param status The error code that was seen.
- */
- void onError(int status);
-
- /**
* Called when the detection fails due to an error occurs in the
* {@link HotwordDetectionService}.
*
@@ -84,6 +78,15 @@ oneway interface IHotwordRecognitionStatusCallback {
in VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure);
/**
+ * Called when the detection fails due to an error occurs in the
+ * {@link com.android.server.soundtrigger.SoundTriggerService}.
+ *
+ * @param soundTriggerFailure It provides the error code, error message and
+ * suggested action.
+ */
+ void onSoundTriggerFailure(in SoundTriggerFailure soundTriggerFailure);
+
+ /**
* Called when the detection fails due to an unknown error occurs.
*
* @param errorMessage It provides the error message.
diff --git a/core/java/com/android/internal/expresslog/Counter.java b/core/java/com/android/internal/expresslog/Counter.java
deleted file mode 100644
index 4a46d91efbf0..000000000000
--- a/core/java/com/android/internal/expresslog/Counter.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.expresslog;
-
-import android.annotation.NonNull;
-
-import com.android.internal.util.FrameworkStatsLog;
-
-/** Counter encapsulates StatsD write API calls */
-public final class Counter {
-
- // Not instantiable.
- private Counter() {}
-
- /**
- * Increments Telemetry Express Counter metric by 1
- * @param metricId to log, no-op if metricId is not defined in the TeX catalog
- * @hide
- */
- public static void logIncrement(@NonNull String metricId) {
- logIncrement(metricId, 1);
- }
-
- /**
- * Increments Telemetry Express Counter metric by 1
- * @param metricId to log, no-op if metricId is not defined in the TeX catalog
- * @param uid used as a dimension for the count metric
- * @hide
- */
- public static void logIncrementWithUid(@NonNull String metricId, int uid) {
- logIncrementWithUid(metricId, uid, 1);
- }
-
- /**
- * Increments Telemetry Express Counter metric by arbitrary value
- * @param metricId to log, no-op if metricId is not defined in the TeX catalog
- * @param amount to increment counter
- * @hide
- */
- public static void logIncrement(@NonNull String metricId, long amount) {
- final long metricIdHash = Utils.hashString(metricId);
- FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_EVENT_REPORTED, metricIdHash, amount);
- }
-
- /**
- * Increments Telemetry Express Counter metric by arbitrary value
- * @param metricId to log, no-op if metricId is not defined in the TeX catalog
- * @param uid used as a dimension for the count metric
- * @param amount to increment counter
- * @hide
- */
- public static void logIncrementWithUid(@NonNull String metricId, int uid, long amount) {
- final long metricIdHash = Utils.hashString(metricId);
- FrameworkStatsLog.write(
- FrameworkStatsLog.EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid);
- }
-}
diff --git a/core/java/com/android/internal/expresslog/Histogram.java b/core/java/com/android/internal/expresslog/Histogram.java
deleted file mode 100644
index 2fe784a5a855..000000000000
--- a/core/java/com/android/internal/expresslog/Histogram.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.expresslog;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-
-import com.android.internal.util.FrameworkStatsLog;
-
-import java.util.Arrays;
-
-/** Histogram encapsulates StatsD write API calls */
-public final class Histogram {
-
- private final long mMetricIdHash;
- private final BinOptions mBinOptions;
-
- /**
- * Creates Histogram metric logging wrapper
- *
- * @param metricId to log, logging will be no-op if metricId is not defined in the TeX catalog
- * @param binOptions to calculate bin index for samples
- * @hide
- */
- public Histogram(@NonNull String metricId, @NonNull BinOptions binOptions) {
- mMetricIdHash = Utils.hashString(metricId);
- mBinOptions = binOptions;
- }
-
- /**
- * Logs increment sample count for automatically calculated bin
- *
- * @param sample value
- * @hide
- */
- public void logSample(float sample) {
- final int binIndex = mBinOptions.getBinForSample(sample);
- FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash,
- /*count*/ 1, binIndex);
- }
-
- /**
- * Logs increment sample count for automatically calculated bin
- *
- * @param uid used as a dimension for the count metric
- * @param sample value
- * @hide
- */
- public void logSampleWithUid(int uid, float sample) {
- final int binIndex = mBinOptions.getBinForSample(sample);
- FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED,
- mMetricIdHash, /*count*/ 1, binIndex, uid);
- }
-
- /** Used by Histogram to map data sample to corresponding bin */
- public interface BinOptions {
- /**
- * Returns bins count to be used by a histogram
- *
- * @return bins count used to initialize Options, including overflow & underflow bins
- * @hide
- */
- int getBinsCount();
-
- /**
- * Returns bin index for the input sample value
- * index == 0 stands for underflow
- * index == getBinsCount() - 1 stands for overflow
- *
- * @return zero based index
- * @hide
- */
- int getBinForSample(float sample);
- }
-
- /** Used by Histogram to map data sample to corresponding bin for uniform bins */
- public static final class UniformOptions implements BinOptions {
-
- private final int mBinCount;
- private final float mMinValue;
- private final float mExclusiveMaxValue;
- private final float mBinSize;
-
- /**
- * Creates options for uniform (linear) sized bins
- *
- * @param binCount amount of histogram bins. 2 bin indexes will be calculated
- * automatically to represent underflow & overflow bins
- * @param minValue is included in the first bin, values less than minValue
- * go to underflow bin
- * @param exclusiveMaxValue is included in the overflow bucket. For accurate
- * measure up to kMax, then exclusiveMaxValue
- * should be set to kMax + 1
- * @hide
- */
- public UniformOptions(@IntRange(from = 1) int binCount, float minValue,
- float exclusiveMaxValue) {
- if (binCount < 1) {
- throw new IllegalArgumentException("Bin count should be positive number");
- }
-
- if (exclusiveMaxValue <= minValue) {
- throw new IllegalArgumentException("Bins range invalid (maxValue < minValue)");
- }
-
- mMinValue = minValue;
- mExclusiveMaxValue = exclusiveMaxValue;
- mBinSize = (mExclusiveMaxValue - minValue) / binCount;
-
- // Implicitly add 2 for the extra underflow & overflow bins
- mBinCount = binCount + 2;
- }
-
- @Override
- public int getBinsCount() {
- return mBinCount;
- }
-
- @Override
- public int getBinForSample(float sample) {
- if (sample < mMinValue) {
- // goes to underflow
- return 0;
- } else if (sample >= mExclusiveMaxValue) {
- // goes to overflow
- return mBinCount - 1;
- }
- return (int) ((sample - mMinValue) / mBinSize + 1);
- }
- }
-
- /** Used by Histogram to map data sample to corresponding bin for scaled bins */
- public static final class ScaledRangeOptions implements BinOptions {
- // store minimum value per bin
- final long[] mBins;
-
- /**
- * Creates options for scaled range bins
- *
- * @param binCount amount of histogram bins. 2 bin indexes will be calculated
- * automatically to represent underflow & overflow bins
- * @param minValue is included in the first bin, values less than minValue
- * go to underflow bin
- * @param firstBinWidth used to represent first bin width and as a reference to calculate
- * width for consecutive bins
- * @param scaleFactor used to calculate width for consecutive bins
- * @hide
- */
- public ScaledRangeOptions(@IntRange(from = 1) int binCount, int minValue,
- @FloatRange(from = 1.f) float firstBinWidth,
- @FloatRange(from = 1.f) float scaleFactor) {
- if (binCount < 1) {
- throw new IllegalArgumentException("Bin count should be positive number");
- }
-
- if (firstBinWidth < 1.f) {
- throw new IllegalArgumentException(
- "First bin width invalid (should be 1.f at minimum)");
- }
-
- if (scaleFactor < 1.f) {
- throw new IllegalArgumentException(
- "Scaled factor invalid (should be 1.f at minimum)");
- }
-
- // precalculating bins ranges (no need to create a bin for underflow reference value)
- mBins = initBins(binCount + 1, minValue, firstBinWidth, scaleFactor);
- }
-
- @Override
- public int getBinsCount() {
- return mBins.length + 1;
- }
-
- @Override
- public int getBinForSample(float sample) {
- if (sample < mBins[0]) {
- // goes to underflow
- return 0;
- } else if (sample >= mBins[mBins.length - 1]) {
- // goes to overflow
- return mBins.length;
- }
-
- return lower_bound(mBins, (long) sample) + 1;
- }
-
- // To find lower bound using binary search implementation of Arrays utility class
- private static int lower_bound(long[] array, long sample) {
- int index = Arrays.binarySearch(array, sample);
- // If key is not present in the array
- if (index < 0) {
- // Index specify the position of the key when inserted in the sorted array
- // so the element currently present at this position will be the lower bound
- return Math.abs(index) - 2;
- }
- return index;
- }
-
- private static long[] initBins(int count, int minValue, float firstBinWidth,
- float scaleFactor) {
- long[] bins = new long[count];
- bins[0] = minValue;
- double lastWidth = firstBinWidth;
- for (int i = 1; i < count; i++) {
- // current bin minValue = previous bin width * scaleFactor
- double currentBinMinValue = bins[i - 1] + lastWidth;
- if (currentBinMinValue > Integer.MAX_VALUE) {
- throw new IllegalArgumentException(
- "Attempted to create a bucket larger than maxint");
- }
-
- bins[i] = (long) currentBinMinValue;
- lastWidth *= scaleFactor;
- }
- return bins;
- }
- }
-}
diff --git a/core/java/com/android/internal/expresslog/OWNERS b/core/java/com/android/internal/expresslog/OWNERS
deleted file mode 100644
index ee865b1e4ec8..000000000000
--- a/core/java/com/android/internal/expresslog/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/stats/OWNERS
diff --git a/core/java/com/android/internal/expresslog/TEST_MAPPING b/core/java/com/android/internal/expresslog/TEST_MAPPING
deleted file mode 100644
index c9b0cf80a1e6..000000000000
--- a/core/java/com/android/internal/expresslog/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "ExpressLogTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index e2c096c11fee..aa6b1c00d186 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -20,7 +20,7 @@ import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.expresslog.Counter;
+import com.android.modules.expresslog.Counter;
import java.io.IOException;
import java.util.Arrays;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index fc26766d7090..6bec6bc236bd 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -225,7 +225,6 @@ cc_library_shared {
"android_security_Scrypt.cpp",
"com_android_internal_content_om_OverlayConfig.cpp",
"com_android_internal_content_om_OverlayManagerImpl.cpp",
- "com_android_internal_expresslog_Utils.cpp",
"com_android_internal_net_NetworkUtilsInternal.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
@@ -262,6 +261,7 @@ cc_library_shared {
"libstatssocket_lazy",
"libskia",
"libtextclassifier_hash_static",
+ "libexpresslog_jni",
],
shared_libs: [
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b550f28e0934..21bdf099f18d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -200,7 +200,7 @@ extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayManagerImpl(JNIEnv* env);
-extern int register_com_android_internal_expresslog_Utils(JNIEnv* env);
+extern int register_com_android_modules_expresslog_Utils(JNIEnv* env);
extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
@@ -1586,7 +1586,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_incremental_IncrementalManager),
REG_JNI(register_com_android_internal_content_om_OverlayConfig),
REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl),
- REG_JNI(register_com_android_internal_expresslog_Utils),
+ REG_JNI(register_com_android_modules_expresslog_Utils),
REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index bce533281186..4e4abec88040 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -104,6 +104,3 @@ per-file com_android_internal_os_*MultiStateCounter* = file:/BATTERY_STATS_OWNER
# PM
per-file com_android_internal_content_* = file:/PACKAGE_MANAGER_OWNERS
-
-# Stats/expresslog
-per-file *expresslog* = file:/services/core/java/com/android/server/stats/OWNERS
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 8ba4eed8b34d..e1be0cd80bb6 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2481,36 +2481,31 @@ static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject th
if (jSurroundFormats == nullptr) {
ALOGE("jSurroundFormats is NULL");
- return (jint)AUDIO_JAVA_BAD_VALUE;
+ return static_cast<jint>(AUDIO_JAVA_BAD_VALUE);
}
if (!env->IsInstanceOf(jSurroundFormats, gMapClass)) {
ALOGE("getSurroundFormats not a map");
- return (jint)AUDIO_JAVA_BAD_VALUE;
+ return static_cast<jint>(AUDIO_JAVA_BAD_VALUE);
}
jint jStatus;
unsigned int numSurroundFormats = 0;
- audio_format_t *surroundFormats = nullptr;
- bool *surroundFormatsEnabled = nullptr;
- status_t status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats,
- surroundFormatsEnabled);
+ status_t status = AudioSystem::getSurroundFormats(&numSurroundFormats, nullptr, nullptr);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
- jStatus = nativeToJavaStatus(status);
- goto exit;
+ return nativeToJavaStatus(status);
}
if (numSurroundFormats == 0) {
- jStatus = (jint)AUDIO_JAVA_SUCCESS;
- goto exit;
+ return static_cast<jint>(AUDIO_JAVA_SUCCESS);
}
- surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
- surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool));
- status = AudioSystem::getSurroundFormats(&numSurroundFormats, surroundFormats,
- surroundFormatsEnabled);
+ auto surroundFormats = std::make_unique<audio_format_t[]>(numSurroundFormats);
+ auto surroundFormatsEnabled = std::make_unique<bool[]>(numSurroundFormats);
+ status = AudioSystem::getSurroundFormats(&numSurroundFormats, &surroundFormats[0],
+ &surroundFormatsEnabled[0]);
jStatus = nativeToJavaStatus(status);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
- goto exit;
+ return jStatus;
}
for (size_t i = 0; i < numSurroundFormats; i++) {
int audioFormat = audioFormatFromNative(surroundFormats[i]);
@@ -2526,9 +2521,6 @@ static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject th
env->DeleteLocalRef(enabled);
}
-exit:
- free(surroundFormats);
- free(surroundFormatsEnabled);
return jStatus;
}
@@ -2538,31 +2530,28 @@ static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jo
if (jSurroundFormats == nullptr) {
ALOGE("jSurroundFormats is NULL");
- return (jint)AUDIO_JAVA_BAD_VALUE;
+ return static_cast<jint>(AUDIO_JAVA_BAD_VALUE);
}
if (!env->IsInstanceOf(jSurroundFormats, gArrayListClass)) {
ALOGE("jSurroundFormats not an arraylist");
- return (jint)AUDIO_JAVA_BAD_VALUE;
+ return static_cast<jint>(AUDIO_JAVA_BAD_VALUE);
}
jint jStatus;
unsigned int numSurroundFormats = 0;
- audio_format_t *surroundFormats = nullptr;
- status_t status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+ status_t status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, nullptr);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status);
- jStatus = nativeToJavaStatus(status);
- goto exit;
+ return nativeToJavaStatus(status);
}
if (numSurroundFormats == 0) {
- jStatus = (jint)AUDIO_JAVA_SUCCESS;
- goto exit;
+ return static_cast<jint>(AUDIO_JAVA_SUCCESS);
}
- surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
- status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, surroundFormats);
+ auto surroundFormats = std::make_unique<audio_format_t[]>(numSurroundFormats);
+ status = AudioSystem::getReportedSurroundFormats(&numSurroundFormats, &surroundFormats[0]);
jStatus = nativeToJavaStatus(status);
if (status != NO_ERROR) {
ALOGE_IF(status != NO_ERROR, "AudioSystem::getReportedSurroundFormats error %d", status);
- goto exit;
+ return jStatus;
}
for (size_t i = 0; i < numSurroundFormats; i++) {
int audioFormat = audioFormatFromNative(surroundFormats[i]);
@@ -2576,8 +2565,6 @@ static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jo
env->DeleteLocalRef(surroundFormat);
}
-exit:
- free(surroundFormats);
return jStatus;
}
diff --git a/core/jni/com_android_internal_expresslog_Utils.cpp b/core/jni/com_android_internal_expresslog_Utils.cpp
deleted file mode 100644
index d33a7bda27f7..000000000000
--- a/core/jni/com_android_internal_expresslog_Utils.cpp
+++ /dev/null
@@ -1,57 +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.
- */
-
-#include <nativehelper/JNIHelp.h>
-#include <utils/hash/farmhash.h>
-
-#include "core_jni_helpers.h"
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-static jclass g_stringClass = nullptr;
-
-/**
- * Class: com_android_internal_expresslog_Utils
- * Method: hashString
- * Signature: (Ljava/lang/String;)J
- */
-static jlong hashString(JNIEnv* env, jclass /*class*/, jstring metricNameObj) {
- ScopedUtfChars name(env, metricNameObj);
- if (name.c_str() == nullptr) {
- return 0;
- }
-
- return static_cast<jlong>(farmhash::Fingerprint64(name.c_str(), name.size()));
-}
-
-static const JNINativeMethod g_methods[] = {
- {"hashString", "(Ljava/lang/String;)J", (void*)hashString},
-};
-
-static const char* const kUtilsPathName = "com/android/internal/expresslog/Utils";
-
-namespace android {
-
-int register_com_android_internal_expresslog_Utils(JNIEnv* env) {
- jclass stringClass = FindClassOrDie(env, "java/lang/String");
- g_stringClass = MakeGlobalRefOrDie(env, stringClass);
-
- return RegisterMethodsOrDie(env, kUtilsPathName, g_methods, NELEM(g_methods));
-}
-
-} // namespace android
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ed612a05e6b5..025a57d0e334 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -1035,6 +1035,7 @@ message AppsExitInfoProto {
optional int32 uid = 1;
repeated .android.app.ApplicationExitInfoProto app_exit_info = 2;
+ repeated .android.app.ApplicationExitInfoProto app_recoverable_crash = 3;
}
repeated User users = 2;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 78d39236b392..05b38a562e29 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1746,37 +1746,6 @@
android:protectionLevel="dangerous"
android:permissionFlags="hardRestricted" />
- <!-- @TestApi Allows an application to access wrist temperature data from the watch sensors.
- <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
- <p>Protection level: dangerous
- @hide
- -->
- <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE"
- android:permissionGroup="android.permission-group.UNDEFINED"
- android:label="@string/permlab_bodySensorsWristTemperature"
- android:description="@string/permdesc_bodySensorsWristTemperature"
- android:backgroundPermission="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
- android:protectionLevel="dangerous" />
-
- <!-- @TestApi Allows an application to access wrist temperature data from the watch sensors.
- If you're requesting this permission, you must also request
- {@link #BODY_SENSORS_WRIST_TEMPERATURE}. Requesting this permission by itself doesn't
- give you wrist temperature body sensors access.
- <p class="note"><strong>Note: </strong> This permission is for Wear OS only.
- <p>Protection level: dangerous
-
- <p> This is a hard restricted permission which cannot be held by an app until
- the installer on record allowlists the permission. For more details see
- {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
- @hide
- -->
- <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND"
- android:permissionGroup="android.permission-group.UNDEFINED"
- android:label="@string/permlab_bodySensors_wristTemperature_background"
- android:description="@string/permdesc_bodySensors_wristTemperature_background"
- android:protectionLevel="dangerous"
- android:permissionFlags="hardRestricted" />
-
<!-- Allows an app to use fingerprint hardware.
<p>Protection level: normal
@deprecated Applications should request {@link
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e4d74b5373e0..9f10ae6066f5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1373,6 +1373,9 @@
<!-- Number of notifications to keep in the notification service historical archive -->
<integer name="config_notificationServiceArchiveSize">100</integer>
+ <!-- List of packages that will be able to use full screen intent in notifications by default -->
+ <string-array name="config_useFullScreenIntentPackages" translatable="false" />
+
<!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml
index 85325fec7277..daa0f553f47a 100644
--- a/core/res/res/values/public-final.xml
+++ b/core/res/res/values/public-final.xml
@@ -3402,4 +3402,343 @@
<!-- @hide @SystemApi -->
<public type="bool" name="config_enableQrCodeScannerOnLockScreen" id="0x01110008" />
+ <!-- ===============================================================
+ Resources added in version U of the platform
+
+ NOTE: After this version of the platform is forked, changes cannot be made to the root
+ branch's groups for that release. Only merge changes to the forked platform branch.
+ =============================================================== -->
+ <eat-comment/>
+
+ <staging-public-group-final type="attr" first-id="0x01ce0000">
+ <public name="handwritingBoundsOffsetLeft" />
+ <public name="handwritingBoundsOffsetTop" />
+ <public name="handwritingBoundsOffsetRight" />
+ <public name="handwritingBoundsOffsetBottom" />
+ <public name="accessibilityDataSensitive" />
+ <public name="enableTextStylingShortcuts" />
+ <public name="requiredDisplayCategory"/>
+ <public name="removed_maxConcurrentSessionsCount" />
+ <public name="visualQueryDetectionService" />
+ <public name="physicalKeyboardHintLanguageTag" />
+ <public name="physicalKeyboardHintLayoutType" />
+ <public name="allowSharedIsolatedProcess" />
+ <public name="keyboardLocale" />
+ <public name="keyboardLayoutType" />
+ <public name="allowUpdateOwnership" />
+ <public name="isCredential"/>
+ <public name="searchResultHighlightColor" />
+ <public name="focusedSearchResultHighlightColor" />
+ <public name="stylusHandwritingSettingsActivity" />
+ <public name="windowNoMoveAnimation" />
+ <public name="settingsSubtitle" />
+ <public name="capability" />
+ </staging-public-group-final>
+
+ <public type="attr" name="handwritingBoundsOffsetLeft" id="0x01010673" />
+ <public type="attr" name="handwritingBoundsOffsetTop" id="0x01010674" />
+ <public type="attr" name="handwritingBoundsOffsetRight" id="0x01010675" />
+ <public type="attr" name="handwritingBoundsOffsetBottom" id="0x01010676" />
+ <public type="attr" name="accessibilityDataSensitive" id="0x01010677" />
+ <public type="attr" name="enableTextStylingShortcuts" id="0x01010678" />
+ <public type="attr" name="requiredDisplayCategory" id="0x01010679" />
+ <public type="attr" name="visualQueryDetectionService" id="0x0101067a" />
+ <public type="attr" name="physicalKeyboardHintLanguageTag" id="0x0101067b" />
+ <public type="attr" name="physicalKeyboardHintLayoutType" id="0x0101067c" />
+ <public type="attr" name="allowSharedIsolatedProcess" id="0x0101067d" />
+ <public type="attr" name="keyboardLocale" id="0x0101067e" />
+ <public type="attr" name="keyboardLayoutType" id="0x0101067f" />
+ <public type="attr" name="allowUpdateOwnership" id="0x01010680" />
+ <public type="attr" name="isCredential" id="0x01010681" />
+ <public type="attr" name="searchResultHighlightColor" id="0x01010682" />
+ <public type="attr" name="focusedSearchResultHighlightColor" id="0x01010683" />
+ <public type="attr" name="stylusHandwritingSettingsActivity" id="0x01010684" />
+ <public type="attr" name="windowNoMoveAnimation" id="0x01010685" />
+ <public type="attr" name="settingsSubtitle" id="0x01010686" />
+ <public type="attr" name="capability" id="0x01010687" />
+
+ <staging-public-group-final type="id" first-id="0x01cd0000">
+ <public name="bold" />
+ <public name="italic" />
+ <public name="underline" />
+ <public name="accessibilityActionScrollInDirection" />
+ </staging-public-group-final>
+
+ <public type="id" name="bold" id="0x0102005b" />
+ <public type="id" name="italic" id="0x0102005c" />
+ <public type="id" name="underline" id="0x0102005d" />
+ <public type="id" name="accessibilityActionScrollInDirection" id="0x0102005e" />
+
+ <staging-public-group-final type="string" first-id="0x01cb0000">
+ <!-- @hide @SystemApi -->
+ <public name="config_systemWearHealthService" />
+ <!-- @hide @SystemApi -->
+ <public name="config_defaultNotes" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemFinancedDeviceController" />
+ <!-- @hide @SystemApi -->
+ <public name="config_systemCallStreaming" />
+ </staging-public-group-final>
+
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_systemWearHealthService" id="0x01040044" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_defaultNotes" id="0x01040045" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_systemFinancedDeviceController" id="0x01040046" />
+ <!-- @hide @SystemApi -->
+ <public type="string" name="config_systemCallStreaming" id="0x01040047" />
+
+ <staging-public-group-final type="dimen" first-id="0x01ca0000">
+ <!-- @hide @SystemApi -->
+ <public name="config_viewConfigurationHandwritingGestureLineMargin" />
+ </staging-public-group-final>
+
+ <!-- @hide @SystemApi -->
+ <public type="dimen" name="config_viewConfigurationHandwritingGestureLineMargin" id="0x0105000a" />
+
+ <staging-public-group-final type="color" first-id="0x01c90000">
+ <public name="system_primary_container_light" />
+ <public name="system_on_primary_container_light" />
+ <public name="system_primary_light" />
+ <public name="system_on_primary_light" />
+ <public name="system_secondary_container_light" />
+ <public name="system_on_secondary_container_light" />
+ <public name="system_secondary_light" />
+ <public name="system_on_secondary_light" />
+ <public name="system_tertiary_container_light" />
+ <public name="system_on_tertiary_container_light" />
+ <public name="system_tertiary_light" />
+ <public name="system_on_tertiary_light" />
+ <public name="system_background_light" />
+ <public name="system_on_background_light" />
+ <public name="system_surface_light" />
+ <public name="system_on_surface_light" />
+ <public name="system_surface_container_low_light" />
+ <public name="system_surface_container_lowest_light" />
+ <public name="system_surface_container_light" />
+ <public name="system_surface_container_high_light" />
+ <public name="system_surface_container_highest_light" />
+ <public name="system_surface_bright_light" />
+ <public name="system_surface_dim_light" />
+ <public name="system_surface_variant_light" />
+ <public name="system_on_surface_variant_light" />
+ <public name="system_outline_light" />
+ <public name="system_error_light" />
+ <public name="system_on_error_light" />
+ <public name="system_error_container_light" />
+ <public name="system_on_error_container_light" />
+ <public name="removed_system_primary_fixed_light" />
+ <public name="removed_system_primary_fixed_dim_light" />
+ <public name="removed_system_on_primary_fixed_light" />
+ <public name="removed_system_on_primary_fixed_variant_light" />
+ <public name="removed_system_secondary_fixed_light" />
+ <public name="removed_system_secondary_fixed_dim_light" />
+ <public name="removed_system_on_secondary_fixed_light" />
+ <public name="removed_system_on_secondary_fixed_variant_light" />
+ <public name="removed_system_tertiary_fixed_light" />
+ <public name="removed_system_tertiary_fixed_dim_light" />
+ <public name="removed_system_on_tertiary_fixed_light" />
+ <public name="removed_system_on_tertiary_fixed_variant_light" />
+ <public name="system_control_activated_light" />
+ <public name="system_control_normal_light" />
+ <public name="system_control_highlight_light" />
+ <public name="system_text_primary_inverse_light" />
+ <public name="system_text_secondary_and_tertiary_inverse_light" />
+ <public name="system_text_primary_inverse_disable_only_light" />
+ <public name="system_text_secondary_and_tertiary_inverse_disabled_light" />
+ <public name="system_text_hint_inverse_light" />
+ <public name="system_palette_key_color_primary_light" />
+ <public name="system_palette_key_color_secondary_light" />
+ <public name="system_palette_key_color_tertiary_light" />
+ <public name="system_palette_key_color_neutral_light" />
+ <public name="system_palette_key_color_neutral_variant_light" />
+ <public name="system_primary_container_dark"/>
+ <public name="system_on_primary_container_dark"/>
+ <public name="system_primary_dark"/>
+ <public name="system_on_primary_dark"/>
+ <public name="system_secondary_container_dark"/>
+ <public name="system_on_secondary_container_dark"/>
+ <public name="system_secondary_dark"/>
+ <public name="system_on_secondary_dark"/>
+ <public name="system_tertiary_container_dark"/>
+ <public name="system_on_tertiary_container_dark"/>
+ <public name="system_tertiary_dark"/>
+ <public name="system_on_tertiary_dark"/>
+ <public name="system_background_dark"/>
+ <public name="system_on_background_dark"/>
+ <public name="system_surface_dark"/>
+ <public name="system_on_surface_dark"/>
+ <public name="system_surface_container_low_dark"/>
+ <public name="system_surface_container_lowest_dark"/>
+ <public name="system_surface_container_dark"/>
+ <public name="system_surface_container_high_dark"/>
+ <public name="system_surface_container_highest_dark"/>
+ <public name="system_surface_bright_dark"/>
+ <public name="system_surface_dim_dark"/>
+ <public name="system_surface_variant_dark"/>
+ <public name="system_on_surface_variant_dark"/>
+ <public name="system_outline_dark"/>
+ <public name="system_error_dark"/>
+ <public name="system_on_error_dark"/>
+ <public name="system_error_container_dark"/>
+ <public name="system_on_error_container_dark"/>
+ <public name="removed_system_primary_fixed_dark"/>
+ <public name="removed_system_primary_fixed_dim_dark"/>
+ <public name="removed_system_on_primary_fixed_dark"/>
+ <public name="removed_system_on_primary_fixed_variant_dark"/>
+ <public name="removed_system_secondary_fixed_dark"/>
+ <public name="removed_system_secondary_fixed_dim_dark"/>
+ <public name="removed_system_on_secondary_fixed_dark"/>
+ <public name="removed_system_on_secondary_fixed_variant_dark"/>
+ <public name="removed_system_tertiary_fixed_dark"/>
+ <public name="removed_system_tertiary_fixed_dim_dark"/>
+ <public name="removed_system_on_tertiary_fixed_dark"/>
+ <public name="removed_system_on_tertiary_fixed_variant_dark"/>
+ <public name="system_control_activated_dark"/>
+ <public name="system_control_normal_dark"/>
+ <public name="system_control_highlight_dark"/>
+ <public name="system_text_primary_inverse_dark"/>
+ <public name="system_text_secondary_and_tertiary_inverse_dark"/>
+ <public name="system_text_primary_inverse_disable_only_dark"/>
+ <public name="system_text_secondary_and_tertiary_inverse_disabled_dark"/>
+ <public name="system_text_hint_inverse_dark"/>
+ <public name="system_palette_key_color_primary_dark"/>
+ <public name="system_palette_key_color_secondary_dark"/>
+ <public name="system_palette_key_color_tertiary_dark"/>
+ <public name="system_palette_key_color_neutral_dark"/>
+ <public name="system_palette_key_color_neutral_variant_dark"/>
+ <public name="system_primary_fixed" />
+ <public name="system_primary_fixed_dim" />
+ <public name="system_on_primary_fixed" />
+ <public name="system_on_primary_fixed_variant" />
+ <public name="system_secondary_fixed" />
+ <public name="system_secondary_fixed_dim" />
+ <public name="system_on_secondary_fixed" />
+ <public name="system_on_secondary_fixed_variant" />
+ <public name="system_tertiary_fixed" />
+ <public name="system_tertiary_fixed_dim" />
+ <public name="system_on_tertiary_fixed" />
+ <public name="system_on_tertiary_fixed_variant" />
+ <public name="system_outline_variant_light" />
+ <public name="system_outline_variant_dark" />
+ </staging-public-group-final>
+
+ <public type="color" name="system_primary_container_light" id="0x0106005e" />
+ <public type="color" name="system_on_primary_container_light" id="0x0106005f" />
+ <public type="color" name="system_primary_light" id="0x01060060" />
+ <public type="color" name="system_on_primary_light" id="0x01060061" />
+ <public type="color" name="system_secondary_container_light" id="0x01060062" />
+ <public type="color" name="system_on_secondary_container_light" id="0x01060063" />
+ <public type="color" name="system_secondary_light" id="0x01060064" />
+ <public type="color" name="system_on_secondary_light" id="0x01060065" />
+ <public type="color" name="system_tertiary_container_light" id="0x01060066" />
+ <public type="color" name="system_on_tertiary_container_light" id="0x01060067" />
+ <public type="color" name="system_tertiary_light" id="0x01060068" />
+ <public type="color" name="system_on_tertiary_light" id="0x01060069" />
+ <public type="color" name="system_background_light" id="0x0106006a" />
+ <public type="color" name="system_on_background_light" id="0x0106006b" />
+ <public type="color" name="system_surface_light" id="0x0106006c" />
+ <public type="color" name="system_on_surface_light" id="0x0106006d" />
+ <public type="color" name="system_surface_container_low_light" id="0x0106006e" />
+ <public type="color" name="system_surface_container_lowest_light" id="0x0106006f" />
+ <public type="color" name="system_surface_container_light" id="0x01060070" />
+ <public type="color" name="system_surface_container_high_light" id="0x01060071" />
+ <public type="color" name="system_surface_container_highest_light" id="0x01060072" />
+ <public type="color" name="system_surface_bright_light" id="0x01060073" />
+ <public type="color" name="system_surface_dim_light" id="0x01060074" />
+ <public type="color" name="system_surface_variant_light" id="0x01060075" />
+ <public type="color" name="system_on_surface_variant_light" id="0x01060076" />
+ <public type="color" name="system_outline_light" id="0x01060077" />
+ <public type="color" name="system_error_light" id="0x01060078" />
+ <public type="color" name="system_on_error_light" id="0x01060079" />
+ <public type="color" name="system_error_container_light" id="0x0106007a" />
+ <public type="color" name="system_on_error_container_light" id="0x0106007b" />
+ <public type="color" name="system_control_activated_light" id="0x0106007c" />
+ <public type="color" name="system_control_normal_light" id="0x0106007d" />
+ <public type="color" name="system_control_highlight_light" id="0x0106007e" />
+ <public type="color" name="system_text_primary_inverse_light" id="0x0106007f" />
+ <public type="color" name="system_text_secondary_and_tertiary_inverse_light" id="0x01060080" />
+ <public type="color" name="system_text_primary_inverse_disable_only_light" id="0x01060081" />
+ <public type="color" name="system_text_secondary_and_tertiary_inverse_disabled_light" id="0x01060082" />
+ <public type="color" name="system_text_hint_inverse_light" id="0x01060083" />
+ <public type="color" name="system_palette_key_color_primary_light" id="0x01060084" />
+ <public type="color" name="system_palette_key_color_secondary_light" id="0x01060085" />
+ <public type="color" name="system_palette_key_color_tertiary_light" id="0x01060086" />
+ <public type="color" name="system_palette_key_color_neutral_light" id="0x01060087" />
+ <public type="color" name="system_palette_key_color_neutral_variant_light" id="0x01060088" />
+ <public type="color" name="system_primary_container_dark" id="0x01060089" />
+ <public type="color" name="system_on_primary_container_dark" id="0x0106008a" />
+ <public type="color" name="system_primary_dark" id="0x0106008b" />
+ <public type="color" name="system_on_primary_dark" id="0x0106008c" />
+ <public type="color" name="system_secondary_container_dark" id="0x0106008d" />
+ <public type="color" name="system_on_secondary_container_dark" id="0x0106008e" />
+ <public type="color" name="system_secondary_dark" id="0x0106008f" />
+ <public type="color" name="system_on_secondary_dark" id="0x01060090" />
+ <public type="color" name="system_tertiary_container_dark" id="0x01060091" />
+ <public type="color" name="system_on_tertiary_container_dark" id="0x01060092" />
+ <public type="color" name="system_tertiary_dark" id="0x01060093" />
+ <public type="color" name="system_on_tertiary_dark" id="0x01060094" />
+ <public type="color" name="system_background_dark" id="0x01060095" />
+ <public type="color" name="system_on_background_dark" id="0x01060096" />
+ <public type="color" name="system_surface_dark" id="0x01060097" />
+ <public type="color" name="system_on_surface_dark" id="0x01060098" />
+ <public type="color" name="system_surface_container_low_dark" id="0x01060099" />
+ <public type="color" name="system_surface_container_lowest_dark" id="0x0106009a" />
+ <public type="color" name="system_surface_container_dark" id="0x0106009b" />
+ <public type="color" name="system_surface_container_high_dark" id="0x0106009c" />
+ <public type="color" name="system_surface_container_highest_dark" id="0x0106009d" />
+ <public type="color" name="system_surface_bright_dark" id="0x0106009e" />
+ <public type="color" name="system_surface_dim_dark" id="0x0106009f" />
+ <public type="color" name="system_surface_variant_dark" id="0x010600a0" />
+ <public type="color" name="system_on_surface_variant_dark" id="0x010600a1" />
+ <public type="color" name="system_outline_dark" id="0x010600a2" />
+ <public type="color" name="system_error_dark" id="0x010600a3" />
+ <public type="color" name="system_on_error_dark" id="0x010600a4" />
+ <public type="color" name="system_error_container_dark" id="0x010600a5" />
+ <public type="color" name="system_on_error_container_dark" id="0x010600a6" />
+ <public type="color" name="system_control_activated_dark" id="0x010600a7" />
+ <public type="color" name="system_control_normal_dark" id="0x010600a8" />
+ <public type="color" name="system_control_highlight_dark" id="0x010600a9" />
+ <public type="color" name="system_text_primary_inverse_dark" id="0x010600aa" />
+ <public type="color" name="system_text_secondary_and_tertiary_inverse_dark" id="0x010600ab" />
+ <public type="color" name="system_text_primary_inverse_disable_only_dark" id="0x010600ac" />
+ <public type="color" name="system_text_secondary_and_tertiary_inverse_disabled_dark" id="0x010600ad" />
+ <public type="color" name="system_text_hint_inverse_dark" id="0x010600ae" />
+ <public type="color" name="system_palette_key_color_primary_dark" id="0x010600af" />
+ <public type="color" name="system_palette_key_color_secondary_dark" id="0x010600b0" />
+ <public type="color" name="system_palette_key_color_tertiary_dark" id="0x010600b1" />
+ <public type="color" name="system_palette_key_color_neutral_dark" id="0x010600b2" />
+ <public type="color" name="system_palette_key_color_neutral_variant_dark" id="0x010600b3" />
+ <public type="color" name="system_primary_fixed" id="0x010600b4" />
+ <public type="color" name="system_primary_fixed_dim" id="0x010600b5" />
+ <public type="color" name="system_on_primary_fixed" id="0x010600b6" />
+ <public type="color" name="system_on_primary_fixed_variant" id="0x010600b7" />
+ <public type="color" name="system_secondary_fixed" id="0x010600b8" />
+ <public type="color" name="system_secondary_fixed_dim" id="0x010600b9" />
+ <public type="color" name="system_on_secondary_fixed" id="0x010600ba" />
+ <public type="color" name="system_on_secondary_fixed_variant" id="0x010600bb" />
+ <public type="color" name="system_tertiary_fixed" id="0x010600bc" />
+ <public type="color" name="system_tertiary_fixed_dim" id="0x010600bd" />
+ <public type="color" name="system_on_tertiary_fixed" id="0x010600be" />
+ <public type="color" name="system_on_tertiary_fixed_variant" id="0x010600bf" />
+ <public type="color" name="system_outline_variant_light" id="0x010600c0" />
+ <public type="color" name="system_outline_variant_dark" id="0x010600c1" />
+
+ <staging-public-group-final type="bool" first-id="0x01be0000">
+ <!-- @hide @SystemApi -->
+ <public name="config_safetyProtectionEnabled" />
+ <!-- @hide @SystemApi -->
+ <public name="config_enableDefaultNotes" />
+ <!-- @hide @SystemApi -->
+ <public name="config_enableDefaultNotesForWorkProfile" />
+ </staging-public-group-final>
+
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_safetyProtectionEnabled" id="0x01110009" />
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_enableDefaultNotes" id="0x0111000a" />
+ <!-- @hide @SystemApi -->
+ <public type="bool" name="config_enableDefaultNotesForWorkProfile" id="0x0111000b" />
+
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 9cbf3b679851..49a19407e59f 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -102,231 +102,65 @@
<resources>
<!-- ===============================================================
- Resources added in version U of the platform
+ Resources added in version NEXT of the platform
NOTE: After this version of the platform is forked, changes cannot be made to the root
branch's groups for that release. Only merge changes to the forked platform branch.
=============================================================== -->
<eat-comment/>
- <staging-public-group type="attr" first-id="0x01ce0000">
- <public name="handwritingBoundsOffsetLeft" />
- <public name="handwritingBoundsOffsetTop" />
- <public name="handwritingBoundsOffsetRight" />
- <public name="handwritingBoundsOffsetBottom" />
- <public name="accessibilityDataSensitive" />
- <public name="enableTextStylingShortcuts" />
- <public name="requiredDisplayCategory"/>
- <public name="removed_maxConcurrentSessionsCount" />
- <public name="visualQueryDetectionService" />
- <public name="physicalKeyboardHintLanguageTag" />
- <public name="physicalKeyboardHintLayoutType" />
- <public name="allowSharedIsolatedProcess" />
- <public name="keyboardLocale" />
- <public name="keyboardLayoutType" />
- <public name="allowUpdateOwnership" />
- <public name="isCredential"/>
- <public name="searchResultHighlightColor" />
- <public name="focusedSearchResultHighlightColor" />
- <public name="stylusHandwritingSettingsActivity" />
- <public name="windowNoMoveAnimation" />
- <public name="settingsSubtitle" />
- <public name="capability" />
+ <staging-public-group type="attr" first-id="0x01bd0000">
</staging-public-group>
- <staging-public-group type="id" first-id="0x01cd0000">
- <public name="bold" />
- <public name="italic" />
- <public name="underline" />
- <public name="accessibilityActionScrollInDirection" />
+ <staging-public-group type="id" first-id="0x01bc0000">
</staging-public-group>
- <staging-public-group type="style" first-id="0x01cc0000">
+ <staging-public-group type="style" first-id="0x01bb0000">
</staging-public-group>
- <staging-public-group type="string" first-id="0x01cb0000">
- <!-- @hide @SystemApi -->
- <public name="config_systemWearHealthService" />
- <!-- @hide @SystemApi -->
- <public name="config_defaultNotes" />
- <!-- @hide @SystemApi -->
- <public name="config_systemFinancedDeviceController" />
- <!-- @hide @SystemApi -->
- <public name="config_systemCallStreaming" />
+ <staging-public-group type="string" first-id="0x01ba0000">
</staging-public-group>
- <staging-public-group type="dimen" first-id="0x01ca0000">
- <!-- @hide @SystemApi -->
- <public name="config_viewConfigurationHandwritingGestureLineMargin" />
+ <staging-public-group type="dimen" first-id="0x01b90000">
</staging-public-group>
- <staging-public-group type="color" first-id="0x01c90000">
- <public name="system_primary_container_light" />
- <public name="system_on_primary_container_light" />
- <public name="system_primary_light" />
- <public name="system_on_primary_light" />
- <public name="system_secondary_container_light" />
- <public name="system_on_secondary_container_light" />
- <public name="system_secondary_light" />
- <public name="system_on_secondary_light" />
- <public name="system_tertiary_container_light" />
- <public name="system_on_tertiary_container_light" />
- <public name="system_tertiary_light" />
- <public name="system_on_tertiary_light" />
- <public name="system_background_light" />
- <public name="system_on_background_light" />
- <public name="system_surface_light" />
- <public name="system_on_surface_light" />
- <public name="system_surface_container_low_light" />
- <public name="system_surface_container_lowest_light" />
- <public name="system_surface_container_light" />
- <public name="system_surface_container_high_light" />
- <public name="system_surface_container_highest_light" />
- <public name="system_surface_bright_light" />
- <public name="system_surface_dim_light" />
- <public name="system_surface_variant_light" />
- <public name="system_on_surface_variant_light" />
- <public name="system_outline_light" />
- <public name="system_error_light" />
- <public name="system_on_error_light" />
- <public name="system_error_container_light" />
- <public name="system_on_error_container_light" />
- <public name="removed_system_primary_fixed_light" />
- <public name="removed_system_primary_fixed_dim_light" />
- <public name="removed_system_on_primary_fixed_light" />
- <public name="removed_system_on_primary_fixed_variant_light" />
- <public name="removed_system_secondary_fixed_light" />
- <public name="removed_system_secondary_fixed_dim_light" />
- <public name="removed_system_on_secondary_fixed_light" />
- <public name="removed_system_on_secondary_fixed_variant_light" />
- <public name="removed_system_tertiary_fixed_light" />
- <public name="removed_system_tertiary_fixed_dim_light" />
- <public name="removed_system_on_tertiary_fixed_light" />
- <public name="removed_system_on_tertiary_fixed_variant_light" />
- <public name="system_control_activated_light" />
- <public name="system_control_normal_light" />
- <public name="system_control_highlight_light" />
- <public name="system_text_primary_inverse_light" />
- <public name="system_text_secondary_and_tertiary_inverse_light" />
- <public name="system_text_primary_inverse_disable_only_light" />
- <public name="system_text_secondary_and_tertiary_inverse_disabled_light" />
- <public name="system_text_hint_inverse_light" />
- <public name="system_palette_key_color_primary_light" />
- <public name="system_palette_key_color_secondary_light" />
- <public name="system_palette_key_color_tertiary_light" />
- <public name="system_palette_key_color_neutral_light" />
- <public name="system_palette_key_color_neutral_variant_light" />
- <public name="system_primary_container_dark"/>
- <public name="system_on_primary_container_dark"/>
- <public name="system_primary_dark"/>
- <public name="system_on_primary_dark"/>
- <public name="system_secondary_container_dark"/>
- <public name="system_on_secondary_container_dark"/>
- <public name="system_secondary_dark"/>
- <public name="system_on_secondary_dark"/>
- <public name="system_tertiary_container_dark"/>
- <public name="system_on_tertiary_container_dark"/>
- <public name="system_tertiary_dark"/>
- <public name="system_on_tertiary_dark"/>
- <public name="system_background_dark"/>
- <public name="system_on_background_dark"/>
- <public name="system_surface_dark"/>
- <public name="system_on_surface_dark"/>
- <public name="system_surface_container_low_dark"/>
- <public name="system_surface_container_lowest_dark"/>
- <public name="system_surface_container_dark"/>
- <public name="system_surface_container_high_dark"/>
- <public name="system_surface_container_highest_dark"/>
- <public name="system_surface_bright_dark"/>
- <public name="system_surface_dim_dark"/>
- <public name="system_surface_variant_dark"/>
- <public name="system_on_surface_variant_dark"/>
- <public name="system_outline_dark"/>
- <public name="system_error_dark"/>
- <public name="system_on_error_dark"/>
- <public name="system_error_container_dark"/>
- <public name="system_on_error_container_dark"/>
- <public name="removed_system_primary_fixed_dark"/>
- <public name="removed_system_primary_fixed_dim_dark"/>
- <public name="removed_system_on_primary_fixed_dark"/>
- <public name="removed_system_on_primary_fixed_variant_dark"/>
- <public name="removed_system_secondary_fixed_dark"/>
- <public name="removed_system_secondary_fixed_dim_dark"/>
- <public name="removed_system_on_secondary_fixed_dark"/>
- <public name="removed_system_on_secondary_fixed_variant_dark"/>
- <public name="removed_system_tertiary_fixed_dark"/>
- <public name="removed_system_tertiary_fixed_dim_dark"/>
- <public name="removed_system_on_tertiary_fixed_dark"/>
- <public name="removed_system_on_tertiary_fixed_variant_dark"/>
- <public name="system_control_activated_dark"/>
- <public name="system_control_normal_dark"/>
- <public name="system_control_highlight_dark"/>
- <public name="system_text_primary_inverse_dark"/>
- <public name="system_text_secondary_and_tertiary_inverse_dark"/>
- <public name="system_text_primary_inverse_disable_only_dark"/>
- <public name="system_text_secondary_and_tertiary_inverse_disabled_dark"/>
- <public name="system_text_hint_inverse_dark"/>
- <public name="system_palette_key_color_primary_dark"/>
- <public name="system_palette_key_color_secondary_dark"/>
- <public name="system_palette_key_color_tertiary_dark"/>
- <public name="system_palette_key_color_neutral_dark"/>
- <public name="system_palette_key_color_neutral_variant_dark"/>
- <public name="system_primary_fixed" />
- <public name="system_primary_fixed_dim" />
- <public name="system_on_primary_fixed" />
- <public name="system_on_primary_fixed_variant" />
- <public name="system_secondary_fixed" />
- <public name="system_secondary_fixed_dim" />
- <public name="system_on_secondary_fixed" />
- <public name="system_on_secondary_fixed_variant" />
- <public name="system_tertiary_fixed" />
- <public name="system_tertiary_fixed_dim" />
- <public name="system_on_tertiary_fixed" />
- <public name="system_on_tertiary_fixed_variant" />
- <public name="system_outline_variant_light" />
- <public name="system_outline_variant_dark" />
+ <staging-public-group type="color" first-id="0x01b80000">
</staging-public-group>
- <staging-public-group type="array" first-id="0x01c80000">
+ <staging-public-group type="array" first-id="0x01b70000">
</staging-public-group>
- <staging-public-group type="drawable" first-id="0x01c70000">
+ <staging-public-group type="drawable" first-id="0x01b60000">
</staging-public-group>
- <staging-public-group type="layout" first-id="0x01c60000">
+ <staging-public-group type="layout" first-id="0x01b50000">
</staging-public-group>
- <staging-public-group type="anim" first-id="0x01c50000">
+ <staging-public-group type="anim" first-id="0x01b40000">
</staging-public-group>
- <staging-public-group type="animator" first-id="0x01c40000">
+ <staging-public-group type="animator" first-id="0x01b30000">
</staging-public-group>
- <staging-public-group type="interpolator" first-id="0x01c30000">
+ <staging-public-group type="interpolator" first-id="0x01b20000">
</staging-public-group>
- <staging-public-group type="mipmap" first-id="0x01c20000">
+ <staging-public-group type="mipmap" first-id="0x01b10000">
</staging-public-group>
- <staging-public-group type="integer" first-id="0x01c10000">
+ <staging-public-group type="integer" first-id="0x01b00000">
</staging-public-group>
- <staging-public-group type="transition" first-id="0x01c00000">
+ <staging-public-group type="transition" first-id="0x01af0000">
</staging-public-group>
- <staging-public-group type="raw" first-id="0x01bf0000">
+ <staging-public-group type="raw" first-id="0x01ae0000">
</staging-public-group>
- <staging-public-group type="bool" first-id="0x01be0000">
- <!-- @hide @SystemApi -->
- <public name="config_safetyProtectionEnabled" />
- <!-- @hide @SystemApi -->
- <public name="config_enableDefaultNotes" />
- <!-- @hide @SystemApi -->
- <public name="config_enableDefaultNotesForWorkProfile" />
+ <staging-public-group type="bool" first-id="0x01ad0000">
</staging-public-group>
- <staging-public-group type="fraction" first-id="0x01bd0000">
+ <staging-public-group type="fraction" first-id="0x01ac0000">
</staging-public-group>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 82314072fbac..947dc2de9841 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1347,16 +1347,6 @@
<!-- Description of the background body sensors permission, listed so the user can decide whether to allow the application to access data from body sensors in the background. [CHAR LIMIT=NONE] -->
<string name="permdesc_bodySensors_background" product="default">Allows the app to access body sensor data, such as heart rate, temperature, and blood oxygen percentage, while the app is in the background.</string>
- <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
- <string name="permlab_bodySensorsWristTemperature">Access body sensor wrist temperature data while the app is in use.</string>
- <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
- <string name="permdesc_bodySensorsWristTemperature" product="default">Allows the app to access body sensor wrist temperature data, while the app is in use.</string>
-
- <!-- Title of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access body sensor wrist temperature data. [CHAR LIMIT=NONE] -->
- <string name="permlab_bodySensors_wristTemperature_background">Access body sensor wrist temperature data while the app is in the background.</string>
- <!-- Description of the body sensors wrist temperature permission, listed so the user can decide whether to allow the application to access data from body sensors. [CHAR LIMIT=NONE] -->
- <string name="permdesc_bodySensors_wristTemperature_background" product="default">Allows the app to access body sensor wrist temperature data, while the app is in the background.</string>
-
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCalendar">Read calendar events and details</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c6c1c8f120d7..a823d1fd8ff4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2017,6 +2017,7 @@
<java-symbol type="integer" name="config_notificationsBatteryMediumARGB" />
<java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" />
<java-symbol type="integer" name="config_notificationServiceArchiveSize" />
+ <java-symbol type="array" name="config_useFullScreenIntentPackages" />
<java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
diff --git a/core/tests/coretests/src/android/app/UiAutomationTest.java b/core/tests/coretests/src/android/app/UiAutomationTest.java
new file mode 100644
index 000000000000..3ac5057bcf24
--- /dev/null
+++ b/core/tests/coretests/src/android/app/UiAutomationTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.UserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class UiAutomationTest {
+
+ private static final int SECONDARY_DISPLAY_ID = 42;
+ private static final int DISPLAY_ID_ASSIGNED_TO_USER = 108;
+
+ private final Looper mLooper = Looper.getMainLooper();
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private IUiAutomationConnection mConnection;
+
+ @Before
+ public void setFixtures() {
+ when(mContext.getMainLooper()).thenReturn(mLooper);
+ mockSystemService(UserManager.class, mUserManager);
+
+ // Set default expectations
+ mockVisibleBackgroundUsersSupported(/* supported= */ false);
+ mockUserVisibility(/* visible= */ true);
+ // make sure it's not used, unless explicitly mocked
+ mockDisplayAssignedToUser(INVALID_DISPLAY);
+ mockContextDisplay(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testContextConstructor_nullContext() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new UiAutomation((Context) null, mConnection));
+ }
+
+ @Test
+ public void testContextConstructor_nullConnection() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new UiAutomation(mContext, (IUiAutomationConnection) null));
+ }
+
+ @Test
+ public void testGetDisplay_contextWithSecondaryDisplayId() {
+ mockContextDisplay(SECONDARY_DISPLAY_ID);
+
+ UiAutomation uiAutomation = new UiAutomation(mContext, mConnection);
+
+ // It's always DEFAULT_DISPLAY regardless, unless the device supports visible bg users
+ assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId())
+ .isEqualTo(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplay_contextWithInvalidDisplayId() {
+ mockContextDisplay(INVALID_DISPLAY);
+
+ UiAutomation uiAutomation = new UiAutomation(mContext, mConnection);
+
+ assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId())
+ .isEqualTo(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testGetDisplay_visibleBgUsers() {
+ mockVisibleBackgroundUsersSupported(/* supported= */ true);
+ mockContextDisplay(SECONDARY_DISPLAY_ID);
+ // Should be using display from context, not from user
+ mockDisplayAssignedToUser(DISPLAY_ID_ASSIGNED_TO_USER);
+
+ UiAutomation uiAutomation = new UiAutomation(mContext, mConnection);
+
+ assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId())
+ .isEqualTo(SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void testGetDisplay_visibleBgUsers_contextWithInvalidDisplayId() {
+ mockVisibleBackgroundUsersSupported(/* supported= */ true);
+ mockContextDisplay(INVALID_DISPLAY);
+ mockDisplayAssignedToUser(DISPLAY_ID_ASSIGNED_TO_USER);
+
+ UiAutomation uiAutomation = new UiAutomation(mContext, mConnection);
+
+ assertWithMessage("getDisplayId()").that(uiAutomation.getDisplayId())
+ .isEqualTo(DISPLAY_ID_ASSIGNED_TO_USER);
+ }
+
+ private <T> void mockSystemService(Class<T> svcClass, T svc) {
+ String svcName = svcClass.getName();
+ when(mContext.getSystemServiceName(svcClass)).thenReturn(svcName);
+ when(mContext.getSystemService(svcName)).thenReturn(svc);
+ }
+
+ private void mockVisibleBackgroundUsersSupported(boolean supported) {
+ when(mUserManager.isVisibleBackgroundUsersSupported()).thenReturn(supported);
+ }
+
+ private void mockContextDisplay(int displayId) {
+ when(mContext.getDisplayId()).thenReturn(displayId);
+ }
+
+ private void mockDisplayAssignedToUser(int displayId) {
+ when(mUserManager.getMainDisplayIdAssignedToUser()).thenReturn(displayId);
+ }
+
+ private void mockUserVisibility(boolean visible) {
+ when(mUserManager.isUserVisible()).thenReturn(visible);
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
new file mode 100644
index 000000000000..66f3bca72aeb
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.Executor;
+
+
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class BiometricPromptTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private IAuthService mService;
+ private BiometricPrompt mBiometricPrompt;
+
+ private CancellationSignal mCancellationSignal;
+
+ private final TestLooper mLooper = new TestLooper();
+ private final Handler mHandler = new Handler(mLooper.getLooper());
+ private final Executor mExecutor = mHandler::post;
+
+ @Before
+ public void setUp() throws RemoteException {
+ mBiometricPrompt = new BiometricPrompt.Builder(mContext)
+ .setUseDefaultSubtitle()
+ .setUseDefaultTitle()
+ .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG
+ | BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ .setService(mService)
+ .build();
+
+ mCancellationSignal = new CancellationSignal();
+ when(mService.authenticate(any(), anyLong(), anyInt(), any(), anyString(), any()))
+ .thenReturn(0L);
+ when(mContext.getPackageName()).thenReturn("BiometricPromptTest");
+ }
+
+ @Test
+ public void testCancellationAfterAuthenticationFailed() throws RemoteException {
+ ArgumentCaptor<IBiometricServiceReceiver> biometricServiceReceiverCaptor =
+ ArgumentCaptor.forClass(IBiometricServiceReceiver.class);
+ BiometricPrompt.AuthenticationCallback callback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ super.onAuthenticationError(errorCode, errString);
+ }};
+ mBiometricPrompt.authenticate(mCancellationSignal, mExecutor, callback);
+ mLooper.dispatchAll();
+
+ verify(mService).authenticate(any(), anyLong(), anyInt(),
+ biometricServiceReceiverCaptor.capture(), anyString(), any());
+
+ biometricServiceReceiverCaptor.getValue().onAuthenticationFailed();
+ mLooper.dispatchAll();
+ mCancellationSignal.cancel();
+
+ verify(mService).cancelAuthentication(any(), anyString(), anyLong());
+ }
+}
diff --git a/core/tests/coretests/src/android/hardware/biometrics/OWNERS b/core/tests/coretests/src/android/hardware/biometrics/OWNERS
new file mode 100644
index 000000000000..6a2192a2c7fb
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/biometrics/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/biometrics/OWNERS
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 3b6e8eaafc22..cde100cc20aa 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -70,7 +70,13 @@ public class WindowOnBackInvokedDispatcherTest {
private ApplicationInfo mApplicationInfo;
private final BackMotionEvent mBackEvent = new BackMotionEvent(
- 0, 0, 0, BackEvent.EDGE_LEFT, null);
+ /* touchX = */ 0,
+ /* touchY = */ 0,
+ /* progress = */ 0,
+ /* velocityX = */ 0,
+ /* velocityY = */ 0,
+ /* swipeEdge = */ BackEvent.EDGE_LEFT,
+ /* departingAnimationTarget = */ null);
@Before
public void setUp() throws Exception {
diff --git a/core/tests/expresslog/AndroidManifest.xml b/core/tests/expresslog/AndroidManifest.xml
deleted file mode 100644
index 94a39e06c974..000000000000
--- a/core/tests/expresslog/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- android:installLocation="internalOnly"
- package="com.android.internal.expresslog" >
-
- <application >
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.internal.expresslog"
- android:label="Telemetry Express Logging Helper Tests" />
-
-</manifest>
diff --git a/core/tests/expresslog/OWNERS b/core/tests/expresslog/OWNERS
deleted file mode 100644
index 3dc958b07f9c..000000000000
--- a/core/tests/expresslog/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 719316
-# Stats/expresslog
-file:/services/core/java/com/android/server/stats/OWNERS
diff --git a/core/tests/expresslog/TEST_MAPPING b/core/tests/expresslog/TEST_MAPPING
deleted file mode 100644
index c9b0cf80a1e6..000000000000
--- a/core/tests/expresslog/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "ExpressLogTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- }
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java b/core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java
deleted file mode 100644
index ee62d7528818..000000000000
--- a/core/tests/expresslog/src/com/android/internal/expresslog/ScaledRangeOptionsTest.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.expresslog;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class ScaledRangeOptionsTest {
- private static final String TAG = ScaledRangeOptionsTest.class.getSimpleName();
-
- @Test
- public void testGetBinsCount() {
- Histogram.ScaledRangeOptions options1 = new Histogram.ScaledRangeOptions(1, 100, 100, 2);
- assertEquals(3, options1.getBinsCount());
-
- Histogram.ScaledRangeOptions options10 = new Histogram.ScaledRangeOptions(10, 100, 100, 2);
- assertEquals(12, options10.getBinsCount());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructZeroBinsCount() {
- new Histogram.ScaledRangeOptions(0, 100, 100, 2);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructNegativeBinsCount() {
- new Histogram.ScaledRangeOptions(-1, 100, 100, 2);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructNegativeFirstBinWidth() {
- new Histogram.ScaledRangeOptions(10, 100, -100, 2);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructTooSmallFirstBinWidth() {
- new Histogram.ScaledRangeOptions(10, 100, 0.5f, 2);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructNegativeScaleFactor() {
- new Histogram.ScaledRangeOptions(10, 100, 100, -2);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructTooSmallScaleFactor() {
- new Histogram.ScaledRangeOptions(10, 100, 100, 0.5f);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructTooBigScaleFactor() {
- new Histogram.ScaledRangeOptions(10, 100, 100, 500.f);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructTooBigBinRange() {
- new Histogram.ScaledRangeOptions(100, 100, 100, 10.f);
- }
-
- @Test
- public void testBinIndexForRangeEqual1() {
- Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(10, 1, 1, 1);
- assertEquals(12, options.getBinsCount());
-
- assertEquals(11, options.getBinForSample(11));
-
- for (int i = 0, bins = options.getBinsCount(); i < bins; i++) {
- assertEquals(i, options.getBinForSample(i));
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual2() {
- // this should produce bin otpions similar to linear histogram with bin width 2
- Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(10, 1, 2, 1);
- assertEquals(12, options.getBinsCount());
-
- for (int i = 0, bins = options.getBinsCount(); i < bins; i++) {
- assertEquals(i, options.getBinForSample(i * 2));
- assertEquals(i, options.getBinForSample(i * 2 - 1));
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual5() {
- Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(2, 0, 5, 1);
- assertEquals(4, options.getBinsCount());
- for (int i = 0; i < 2; i++) {
- for (int sample = 0; sample < 5; sample++) {
- assertEquals(i + 1, options.getBinForSample(i * 5 + sample));
- }
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual10() {
- Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(10, 1, 10, 1);
- assertEquals(0, options.getBinForSample(0));
- assertEquals(options.getBinsCount() - 2, options.getBinForSample(100));
- assertEquals(options.getBinsCount() - 1, options.getBinForSample(101));
-
- final float binSize = (101 - 1) / 10f;
- for (int i = 1, bins = options.getBinsCount() - 1; i < bins; i++) {
- assertEquals(i, options.getBinForSample(i * binSize));
- }
- }
-
- @Test
- public void testBinIndexForScaleFactor2() {
- final int binsCount = 10;
- final int minValue = 10;
- final int firstBinWidth = 5;
- final int scaledFactor = 2;
-
- Histogram.ScaledRangeOptions options = new Histogram.ScaledRangeOptions(
- binsCount, minValue, firstBinWidth, scaledFactor);
- assertEquals(binsCount + 2, options.getBinsCount());
- long[] binCounts = new long[10];
-
- // precalculate max valid value - start value for the overflow bin
- int lastBinStartValue = minValue; //firstBinMin value
- int lastBinWidth = firstBinWidth;
- for (int binIdx = 2; binIdx <= binsCount + 1; binIdx++) {
- lastBinStartValue = lastBinStartValue + lastBinWidth;
- lastBinWidth *= scaledFactor;
- }
-
- // underflow bin
- for (int i = 1; i < minValue; i++) {
- assertEquals(0, options.getBinForSample(i));
- }
-
- for (int i = 10; i < lastBinStartValue; i++) {
- assertTrue(options.getBinForSample(i) > 0);
- assertTrue(options.getBinForSample(i) <= binsCount);
- binCounts[options.getBinForSample(i) - 1]++;
- }
-
- // overflow bin
- assertEquals(binsCount + 1, options.getBinForSample(lastBinStartValue));
-
- for (int i = 1; i < binsCount; i++) {
- assertEquals(binCounts[i], binCounts[i - 1] * 2L);
- }
- }
-}
diff --git a/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java b/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java
deleted file mode 100644
index 037dbb32c2f8..000000000000
--- a/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.expresslog;
-
-import androidx.test.filters.SmallTest;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class UniformOptionsTest {
- private static final String TAG = UniformOptionsTest.class.getSimpleName();
-
- @Test
- public void testGetBinsCount() {
- Histogram.UniformOptions options1 = new Histogram.UniformOptions(1, 100, 1000);
- assertEquals(3, options1.getBinsCount());
-
- Histogram.UniformOptions options10 = new Histogram.UniformOptions(10, 100, 1000);
- assertEquals(12, options10.getBinsCount());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructZeroBinsCount() {
- new Histogram.UniformOptions(0, 100, 1000);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructNegativeBinsCount() {
- new Histogram.UniformOptions(-1, 100, 1000);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructMaxValueLessThanMinValue() {
- new Histogram.UniformOptions(10, 1000, 100);
- }
-
- @Test
- public void testBinIndexForRangeEqual1() {
- Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 11);
- for (int i = 0, bins = options.getBinsCount(); i < bins; i++) {
- assertEquals(i, options.getBinForSample(i));
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual2() {
- Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 21);
- for (int i = 0, bins = options.getBinsCount(); i < bins; i++) {
- assertEquals(i, options.getBinForSample(i * 2));
- assertEquals(i, options.getBinForSample(i * 2 - 1));
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual5() {
- Histogram.UniformOptions options = new Histogram.UniformOptions(2, 0, 10);
- assertEquals(4, options.getBinsCount());
- for (int i = 0; i < 2; i++) {
- for (int sample = 0; sample < 5; sample++) {
- assertEquals(i + 1, options.getBinForSample(i * 5 + sample));
- }
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual10() {
- Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 101);
- assertEquals(0, options.getBinForSample(0));
- assertEquals(options.getBinsCount() - 2, options.getBinForSample(100));
- assertEquals(options.getBinsCount() - 1, options.getBinForSample(101));
-
- final float binSize = (101 - 1) / 10f;
- for (int i = 1, bins = options.getBinsCount() - 1; i < bins; i++) {
- assertEquals(i, options.getBinForSample(i * binSize));
- }
- }
-
- @Test
- public void testBinIndexForRangeEqual90() {
- final int binCount = 10;
- final int minValue = 100;
- final int maxValue = 100000;
-
- Histogram.UniformOptions options = new Histogram.UniformOptions(binCount, minValue,
- maxValue);
-
- // logging underflow sample
- assertEquals(0, options.getBinForSample(minValue - 1));
-
- // logging overflow sample
- assertEquals(binCount + 1, options.getBinForSample(maxValue));
- assertEquals(binCount + 1, options.getBinForSample(maxValue + 1));
-
- // logging min edge sample
- assertEquals(1, options.getBinForSample(minValue));
-
- // logging max edge sample
- assertEquals(binCount, options.getBinForSample(maxValue - 1));
-
- // logging single valid sample per bin
- final int binSize = (maxValue - minValue) / binCount;
-
- for (int i = 0; i < binCount; i++) {
- assertEquals(i + 1, options.getBinForSample(minValue + binSize * i));
- }
- }
-}
diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
index 99594336999b..2fd65dc29363 100644
--- a/data/etc/preinstalled-packages-platform-overlays.xml
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -56,6 +56,9 @@
<install-in-user-type package="com.android.internal.systemui.navbar.transparent">
<install-in user-type="FULL" />
</install-in-user-type>
+ <install-in-user-type package="com.android.role.notes.enabled">
+ <install-in user-type="FULL" />
+ </install-in-user-type>
<install-in-user-type package="com.android.theme.color.amethyst">
<install-in user-type="FULL" />
<install-in user-type="PROFILE" />
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 0b29973507d2..56c3068fe5e9 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -2083,32 +2083,29 @@ public final class ImageDecoder implements AutoCloseable {
}
sIsP010SupportedForAV1Initialized = true;
-
- if (hasHardwareDecoder("video/av01")) {
- sIsP010SupportedForAV1 = true;
- return true;
- }
-
- sIsP010SupportedForAV1 = Build.VERSION.DEVICE_INITIAL_SDK_INT
- >= Build.VERSION_CODES.S;
- return sIsP010SupportedForAV1;
+ return sIsP010SupportedForAV1 = isP010SupportedforMime("video/av01");
}
}
/**
- * Checks if the device has hardware decoder for the target mime type.
- */
- private static boolean hasHardwareDecoder(String mime) {
- final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- for (MediaCodecInfo info : sMCL.getCodecInfos()) {
- if (info.isEncoder() == false && info.isHardwareAccelerated()) {
- try {
- if (info.getCapabilitiesForType(mime) != null) {
- return true;
- }
- } catch (IllegalArgumentException e) {
- // mime is not supported
- return false;
+ * Checks if the device supports decoding 10-bit for the given mime type.
+ */
+ private static boolean isP010SupportedforMime(String mime) {
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) {
+ if (mediaCodecInfo.isEncoder()) {
+ continue;
+ }
+ for (String mediaType : mediaCodecInfo.getSupportedTypes()) {
+ if (mediaType.equalsIgnoreCase(mime)) {
+ MediaCodecInfo.CodecCapabilities codecCapabilities =
+ mediaCodecInfo.getCapabilitiesForType(mediaType);
+ for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) {
+ if (codecCapabilities.colorFormats[i]
+ == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) {
+ return true;
+ }
+ }
}
}
}
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index a5b192cd7ceb..abe8f859f2fe 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -55,20 +55,6 @@ prebuilt_etc {
}
// Extensions
-// NOTE: This module is still under active development and must not
-// be used in production. Use 'androidx.window.sidecar' instead.
-android_library_import {
- name: "window-extensions",
- aars: ["window-extensions-release.aar"],
- sdk_version: "current",
-}
-
-android_library_import {
- name: "window-extensions-core",
- aars: ["window-extensions-core-release.aar"],
- sdk_version: "current",
-}
-
java_library {
name: "androidx.window.extensions",
srcs: [
@@ -77,8 +63,8 @@ java_library {
"src/androidx/window/common/**/*.java",
],
static_libs: [
- "window-extensions",
- "window-extensions-core",
+ "androidx.window.extensions_extensions-nodeps",
+ "androidx.window.extensions.core_core-nodeps",
],
installable: true,
sdk_version: "core_platform",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 8386131b177d..a7a6b3c92157 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -122,16 +122,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
addWindowLayoutInfoListener(activity, extConsumer);
}
- @Override
- public void addWindowLayoutInfoListener(@NonNull @UiContext Context context,
- @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
- final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
- synchronized (mLock) {
- mJavaToExtConsumers.put(consumer, extConsumer);
- }
- addWindowLayoutInfoListener(context, extConsumer);
- }
-
/**
* Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but
* takes a UI Context as a parameter.
diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
deleted file mode 100644
index 96ff840b984b..000000000000
--- a/libs/WindowManager/Jetpack/window-extensions-core-release.aar
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
deleted file mode 100644
index c3b6916121d0..000000000000
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index e84a78f42616..133fd87a2f63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -33,12 +33,19 @@ public interface BackAnimation {
*
* @param touchX the X touch position of the {@link MotionEvent}.
* @param touchY the Y touch position of the {@link MotionEvent}.
+ * @param velocityX the X velocity computed from the {@link MotionEvent}.
+ * @param velocityY the Y velocity computed from the {@link MotionEvent}.
* @param keyAction the original {@link KeyEvent#getAction()} when the event was dispatched to
* the process. This is forwarded separately because the input pipeline may mutate
* the {#event} action state later.
* @param swipeEdge the edge from which the swipe begins.
*/
- void onBackMotion(float touchX, float touchY, int keyAction,
+ void onBackMotion(
+ float touchX,
+ float touchY,
+ float velocityX,
+ float velocityY,
+ int keyAction,
@BackEvent.SwipeEdge int swipeEdge);
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 210c9aab14d6..47d3a5c52074 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -256,8 +256,20 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private class BackAnimationImpl implements BackAnimation {
@Override
public void onBackMotion(
- float touchX, float touchY, int keyAction, @BackEvent.SwipeEdge int swipeEdge) {
- mShellExecutor.execute(() -> onMotionEvent(touchX, touchY, keyAction, swipeEdge));
+ float touchX,
+ float touchY,
+ float velocityX,
+ float velocityY,
+ int keyAction,
+ @BackEvent.SwipeEdge int swipeEdge
+ ) {
+ mShellExecutor.execute(() -> onMotionEvent(
+ /* touchX = */ touchX,
+ /* touchY = */ touchY,
+ /* velocityX = */ velocityX,
+ /* velocityY = */ velocityY,
+ /* keyAction = */ keyAction,
+ /* swipeEdge = */ swipeEdge));
}
@Override
@@ -332,13 +344,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Called when a new motion event needs to be transferred to this
* {@link BackAnimationController}
*/
- public void onMotionEvent(float touchX, float touchY, int keyAction,
+ public void onMotionEvent(
+ float touchX,
+ float touchY,
+ float velocityX,
+ float velocityY,
+ int keyAction,
@BackEvent.SwipeEdge int swipeEdge) {
if (mPostCommitAnimationInProgress) {
return;
}
- mTouchTracker.update(touchX, touchY);
+ mTouchTracker.update(touchX, touchY, velocityX, velocityY);
if (keyAction == MotionEvent.ACTION_DOWN) {
if (!mBackGestureStarted) {
mShouldStartOnNextMoveEvent = true;
@@ -561,6 +578,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
if (runner.isWaitingAnimation()) {
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
+ // Supposed it is in post commit animation state, and start the timeout to watch
+ // if the animation is ready.
+ mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
return;
} else if (runner.isAnimationCancelled()) {
invokeOrCancelBack();
@@ -577,6 +597,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mPostCommitAnimationInProgress) {
return;
}
+
+ mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startPostCommitAnimation()");
mPostCommitAnimationInProgress = true;
mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
@@ -595,9 +617,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
*/
@VisibleForTesting
void onBackAnimationFinished() {
- if (!mPostCommitAnimationInProgress) {
- return;
- }
// Stop timeout runner.
mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
mPostCommitAnimationInProgress = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 695ef4e66302..904574b08562 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -42,11 +42,13 @@ class TouchTracker {
*/
private float mInitTouchX;
private float mInitTouchY;
+ private float mLatestVelocityX;
+ private float mLatestVelocityY;
private float mStartThresholdX;
private int mSwipeEdge;
private boolean mCancelled;
- void update(float touchX, float touchY) {
+ void update(float touchX, float touchY, float velocityX, float velocityY) {
/**
* If back was previously cancelled but the user has started swiping in the forward
* direction again, restart back.
@@ -58,6 +60,8 @@ class TouchTracker {
}
mLatestTouchX = touchX;
mLatestTouchY = touchY;
+ mLatestVelocityX = velocityX;
+ mLatestVelocityY = velocityY;
}
void setTriggerBack(boolean triggerBack) {
@@ -84,7 +88,14 @@ class TouchTracker {
}
BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
- return new BackMotionEvent(mInitTouchX, mInitTouchY, 0, mSwipeEdge, target);
+ return new BackMotionEvent(
+ /* touchX = */ mInitTouchX,
+ /* touchY = */ mInitTouchY,
+ /* progress = */ 0,
+ /* velocityX = */ 0,
+ /* velocityY = */ 0,
+ /* swipeEdge = */ mSwipeEdge,
+ /* departingAnimationTarget = */ target);
}
BackMotionEvent createProgressEvent() {
@@ -111,7 +122,14 @@ class TouchTracker {
}
BackMotionEvent createProgressEvent(float progress) {
- return new BackMotionEvent(mLatestTouchX, mLatestTouchY, progress, mSwipeEdge, null);
+ return new BackMotionEvent(
+ /* touchX = */ mLatestTouchX,
+ /* touchY = */ mLatestTouchY,
+ /* progress = */ progress,
+ /* velocityX = */ mLatestVelocityX,
+ /* velocityY = */ mLatestVelocityY,
+ /* swipeEdge = */ mSwipeEdge,
+ /* departingAnimationTarget = */ null);
}
public void setProgressThreshold(float progressThreshold) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 670b24c176b5..0400963a47e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -25,6 +25,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
+import android.graphics.Point
import android.graphics.Rect
import android.os.IBinder
import android.os.SystemProperties
@@ -193,6 +194,21 @@ class DesktopTasksController(
}
}
+
+ /**
+ * Move a task to fullscreen after being dragged from fullscreen and released back into
+ * status bar area
+ */
+ fun cancelMoveToFreeform(task: RunningTaskInfo, startPosition: Point) {
+ val wct = WindowContainerTransaction()
+ addMoveToFullscreenChanges(wct, task.token)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, startPosition)
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+ }
+
fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) {
val wct = WindowContainerTransaction()
addMoveToFullscreenChanges(wct, task.token)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 3df2340d4524..27eda16f4171 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -17,11 +17,13 @@
package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -55,6 +57,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
public static final int FREEFORM_ANIMATION_DURATION = 336;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
+ private Point mStartPosition;
public EnterDesktopTaskTransitionHandler(
Transitions transitions) {
@@ -79,6 +82,17 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
mPendingTransitionTokens.add(token);
}
+ /**
+ * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+ * @param wct WindowContainerTransaction for transition
+ * @param startPosition Position of task when transition is triggered
+ */
+ public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct,
+ Point startPosition) {
+ mStartPosition = startPosition;
+ startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct);
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
@@ -173,6 +187,37 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
return true;
}
+ if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && mStartPosition != null) {
+ // This Transition animates a task to fullscreen after being dragged from the status
+ // bar and then released back into the status bar area
+ final SurfaceControl sc = change.getLeash();
+ startT.setWindowCrop(sc, null);
+ startT.apply();
+
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setFloatValues(DRAG_FREEFORM_SCALE, 1f);
+ animator.setDuration(FREEFORM_ANIMATION_DURATION);
+ final SurfaceControl.Transaction t = mTransactionSupplier.get();
+ animator.addUpdateListener(animation -> {
+ final float scale = animation.getAnimatedFraction();
+ t.setPosition(sc, mStartPosition.x * (1 - scale),
+ mStartPosition.y * (1 - scale));
+ t.setScale(sc, scale, scale);
+ t.apply();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTransitions.getMainExecutor().execute(
+ () -> finishCallback.onTransitionFinished(null, null));
+ }
+ });
+ animator.start();
+ return true;
+ }
+
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index f70df833cf4f..8c98c77a29ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -30,14 +30,17 @@ import android.app.TaskInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
+import android.os.SystemClock;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
import java.lang.annotation.Retention;
@@ -61,6 +64,14 @@ public class PipAnimationController {
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationType {}
+ /**
+ * The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if
+ * another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button
+ * navigation, then the alpha type is unexpected. So use a timeout to avoid applying wrong
+ * animation style to an unrelated task.
+ */
+ private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 800;
+
public static final int TRANSITION_DIRECTION_NONE = 0;
public static final int TRANSITION_DIRECTION_SAME = 1;
public static final int TRANSITION_DIRECTION_TO_PIP = 2;
@@ -109,6 +120,9 @@ public class PipAnimationController {
});
private PipTransitionAnimator mCurrentAnimator;
+ @AnimationType
+ private int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ private long mLastOneShotAlphaAnimationTime;
public PipAnimationController(PipSurfaceTransactionHelper helper) {
mSurfaceTransactionHelper = helper;
@@ -222,6 +236,37 @@ public class PipAnimationController {
}
/**
+ * Sets the preferred enter animation type for one time. This is typically used to set the
+ * animation type to {@link PipAnimationController#ANIM_TYPE_ALPHA}.
+ * <p>
+ * For example, gesture navigation would first fade out the PiP activity, and the transition
+ * should be responsible to animate in (such as fade in) the PiP.
+ */
+ public void setOneShotEnterAnimationType(@AnimationType int animationType) {
+ mOneShotAnimationType = animationType;
+ if (animationType == ANIM_TYPE_ALPHA) {
+ mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis();
+ }
+ }
+
+ /** Returns the preferred animation type and consumes the one-shot type if needed. */
+ @AnimationType
+ public int takeOneShotEnterAnimationType() {
+ final int type = mOneShotAnimationType;
+ if (type == ANIM_TYPE_ALPHA) {
+ // Restore to default type.
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ if (SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime
+ > ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "Alpha animation is expired. Use bounds animation.");
+ return ANIM_TYPE_BOUNDS;
+ }
+ }
+ return type;
+ }
+
+ /**
* Additional callback interface for PiP animation
*/
public static class PipAnimationCallback {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index a0bd064149d2..5670fe6eaeba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -62,7 +62,6 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
@@ -111,12 +110,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
- /**
- * The alpha type is set for swiping to home. But the swiped task may not enter PiP. And if
- * another task enters PiP by non-swipe ways, e.g. call API in foreground or switch to 3-button
- * navigation, then the alpha type is unexpected.
- */
- private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
/**
* The fixed start delay in ms when fading out the content overlay from bounds animation.
@@ -301,8 +294,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private WindowContainerToken mToken;
private SurfaceControl mLeash;
protected PipTransitionState mPipTransitionState;
- private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
- private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
protected PictureInPictureParams mPictureInPictureParams;
@@ -422,18 +413,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
- * Sets the preferred animation type for one time.
- * This is typically used to set the animation type to
- * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
- */
- public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
- mOneShotAnimationType = animationType;
- if (animationType == ANIM_TYPE_ALPHA) {
- mLastOneShotAlphaAnimationTime = SystemClock.uptimeMillis();
- }
- }
-
- /**
* Override if the PiP should always use a fade-in animation during PiP entry.
*
* @return true if the mOneShotAnimationType should always be
@@ -733,26 +712,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- if (mOneShotAnimationType == ANIM_TYPE_ALPHA
- && SystemClock.uptimeMillis() - mLastOneShotAlphaAnimationTime
- > ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Alpha animation is expired. Use bounds animation.", TAG);
- mOneShotAnimationType = ANIM_TYPE_BOUNDS;
- }
-
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
// For Shell transition, we will animate the window in PipTransition#startAnimation
// instead of #onTaskAppeared.
return;
}
- if (shouldAlwaysFadeIn()) {
- mOneShotAnimationType = ANIM_TYPE_ALPHA;
- }
-
+ final int animationType = shouldAlwaysFadeIn()
+ ? ANIM_TYPE_ALPHA
+ : mPipAnimationController.takeOneShotEnterAnimationType();
if (mWaitForFixedRotation) {
- onTaskAppearedWithFixedRotation();
+ onTaskAppearedWithFixedRotation(animationType);
return;
}
@@ -763,7 +733,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
- if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+ if (animationType == ANIM_TYPE_BOUNDS) {
if (!shouldAttachMenuEarly()) {
mPipMenuController.attach(mLeash);
}
@@ -773,16 +743,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
null /* updateBoundsCallback */);
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
- } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ } else if (animationType == ANIM_TYPE_ALPHA) {
enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
- mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
- throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
+ throw new RuntimeException("Unrecognized animation type: " + animationType);
}
}
- private void onTaskAppearedWithFixedRotation() {
- if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ private void onTaskAppearedWithFixedRotation(int animationType) {
+ if (animationType == ANIM_TYPE_ALPHA) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Defer entering PiP alpha animation, fixed rotation is ongoing", TAG);
// If deferred, hside the surface till fixed rotation is completed.
@@ -791,7 +760,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
tx.setAlpha(mLeash, 0f);
tx.show(mLeash);
tx.apply();
- mOneShotAnimationType = ANIM_TYPE_BOUNDS;
return;
}
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -1895,7 +1863,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
- pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 4a76a502462c..b743140b2403 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -87,7 +87,7 @@ public class PipTransition extends PipTransitionController {
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
- private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ private @PipAnimationController.AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private SurfaceControl.Transaction mFinishTransaction;
private final Rect mExitDestinationBounds = new Rect();
@@ -133,20 +133,6 @@ public class PipTransition extends PipTransitionController {
}
@Override
- public void setIsFullAnimation(boolean isFullAnimation) {
- setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA);
- }
-
- /**
- * Sets the preferred animation type for one time.
- * This is typically used to set the animation type to
- * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
- */
- private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
- mOneShotAnimationType = animationType;
- }
-
- @Override
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
if (destinationBounds != null) {
@@ -288,7 +274,10 @@ public class PipTransition extends PipTransitionController {
if (!requestHasPipEnter(request)) {
throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
}
- if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ mEnterAnimationType = mPipOrganizer.shouldAlwaysFadeIn()
+ ? ANIM_TYPE_ALPHA
+ : mPipAnimationController.takeOneShotEnterAnimationType();
+ if (mEnterAnimationType == ANIM_TYPE_ALPHA) {
mRequestedEnterTransition = transition;
mRequestedEnterTask = request.getTriggerTask().token;
outWCT.setActivityWindowingMode(request.getTriggerTask().token,
@@ -308,7 +297,7 @@ public class PipTransition extends PipTransitionController {
@Override
public boolean handleRotateDisplay(int startRotation, int endRotation,
WindowContainerTransaction wct) {
- if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ if (mRequestedEnterTransition != null && mEnterAnimationType == ANIM_TYPE_ALPHA) {
// A fade-in was requested but not-yet started. In this case, just recalculate the
// initial state under the new rotation.
int rotationDelta = deltaRotation(startRotation, endRotation);
@@ -760,7 +749,6 @@ public class PipTransition extends PipTransitionController {
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
- mOneShotAnimationType = ANIM_TYPE_BOUNDS;
final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay;
startTransaction.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
.setPosition(leash, destinationBounds.left, destinationBounds.top)
@@ -796,17 +784,16 @@ public class PipTransition extends PipTransitionController {
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
}
- if (mPipOrganizer.shouldAlwaysFadeIn()) {
- mOneShotAnimationType = ANIM_TYPE_ALPHA;
- }
-
- if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ final int enterAnimationType = mEnterAnimationType;
+ if (enterAnimationType == ANIM_TYPE_ALPHA) {
+ // Restore to default type.
+ mEnterAnimationType = ANIM_TYPE_BOUNDS;
startTransaction.setAlpha(leash, 0f);
}
startTransaction.apply();
PipAnimationController.PipTransitionAnimator animator;
- if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+ if (enterAnimationType == ANIM_TYPE_BOUNDS) {
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, rotationDelta);
@@ -829,13 +816,11 @@ public class PipTransition extends PipTransitionController {
animator.setColorContentOverlay(mContext);
}
}
- } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ } else if (enterAnimationType == ANIM_TYPE_ALPHA) {
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
0f, 1f);
- mOneShotAnimationType = ANIM_TYPE_BOUNDS;
} else {
- throw new RuntimeException("Unrecognized animation type: "
- + mOneShotAnimationType);
+ throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
@@ -897,7 +882,7 @@ public class PipTransition extends PipTransitionController {
.setWindowCrop(leash, endBounds.width(), endBounds.height());
}
}
- mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
+ mSplitScreenOptional.get().finishEnterSplitScreen(finishTransaction);
startTransaction.apply();
mPipOrganizer.onExitPipFinished(taskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index f51e247fe112..7979ce7a80c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -105,15 +105,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/**
- * Called to inform the transition that the animation should start with the assumption that
- * PiP is not animating from its original bounds, but rather a continuation of another
- * animation. For example, gesture navigation would first fade out the PiP activity, and the
- * transition should be responsible to animate in (such as fade in) the PiP.
- */
- public void setIsFullAnimation(boolean isFullAnimation) {
- }
-
- /**
* Called when the Shell wants to start an exit Pip transition/animation.
*/
public void startExitTransition(int type, WindowContainerTransaction out,
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 463ad77d828f..b0bb14b49db6 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
@@ -968,12 +968,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsState.setShelfVisibility(visible, shelfHeight);
}
- private void setPinnedStackAnimationType(int animationType) {
- mPipTaskOrganizer.setOneShotAnimationType(animationType);
- mPipTransitionController.setIsFullAnimation(
- animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
- }
-
@VisibleForTesting
void setPinnedStackAnimationListener(PipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback;
@@ -1337,7 +1331,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
@Override
public void setPipAnimationTypeToAlpha() {
executeRemoteCallWithTaskPermission(mController, "setPipAnimationTypeToAlpha",
- (controller) -> controller.setPinnedStackAnimationType(ANIM_TYPE_ALPHA));
+ (controller) -> controller.mPipAnimationController.setOneShotEnterAnimationType(
+ ANIM_TYPE_ALPHA));
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 81e118a31b73..f819bee2d5e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -129,9 +129,10 @@ interface ISplitScreen {
/**
* Start a pair of intents in one transition.
*/
- oneway void startIntents(in PendingIntent pendingIntent1, in Bundle options1,
- in PendingIntent pendingIntent2, in Bundle options2, int splitPosition,
- float splitRatio, in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
+ oneway void startIntents(in PendingIntent pendingIntent1, in ShortcutInfo shortcutInfo1,
+ in Bundle options1, in PendingIntent pendingIntent2, in ShortcutInfo shortcutInfo2,
+ in Bundle options2, int splitPosition, float splitRatio,
+ in RemoteTransition remoteTransition, in InstanceId instanceId) = 19;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 7d5ab8428a3e..2cd16be9590c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -626,6 +626,35 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
splitPosition, splitRatio, adapter, instanceId);
}
+ private void startIntents(PendingIntent pendingIntent1,
+ @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+ PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+ @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ Intent fillInIntent1 = null;
+ Intent fillInIntent2 = null;
+ final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
+ final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ if (samePackage(packageName1, packageName2)) {
+ if (supportMultiInstancesSplit(packageName1)) {
+ fillInIntent1 = new Intent();
+ fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ fillInIntent2 = new Intent();
+ fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ } else {
+ pendingIntent2 = null;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Cancel entering split as not supporting multi-instances");
+ Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1,
+ pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio,
+ remoteTransition, instanceId);
+ }
+
@Override
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
@@ -1066,11 +1095,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
- public void startIntents(PendingIntent pendingIntent1, @Nullable Bundle options1,
- PendingIntent pendingIntent2, @Nullable Bundle options2,
+ public void startIntents(PendingIntent pendingIntent1, @Nullable ShortcutInfo shortcutInfo1,
+ @Nullable Bundle options1, PendingIntent pendingIntent2,
+ @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
@SplitPosition int splitPosition, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
- // TODO(b/259368992): To be implemented.
+ executeRemoteCallWithTaskPermission(mController, "startIntents",
+ (controller) ->
+ controller.startIntents(pendingIntent1, shortcutInfo1,
+ options1, pendingIntent2, shortcutInfo2, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId)
+ );
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 22800ad8e8a8..8b890bba20b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -22,6 +22,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
@@ -155,8 +156,10 @@ class SplitScreenTransitions {
}
boolean isRootOrSplitSideRoot = change.getParent() == null
|| topRoot.equals(change.getParent());
- // For enter or exit, we only want to animate the side roots but not the top-root.
- if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer())) {
+ boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR;
+ // For enter or exit, we only want to animate side roots and the divider but not the
+ // top-root.
+ if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer()) || isDivider) {
continue;
}
@@ -165,6 +168,10 @@ class SplitScreenTransitions {
t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
+ } else if (isDivider) {
+ t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
+ t.setLayer(leash, Integer.MAX_VALUE);
+ t.show(leash);
}
boolean isOpening = isOpeningTransition(info);
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 04cb17c6a735..ce5a2af65646 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -682,6 +682,46 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setEnterInstanceId(instanceId);
}
+ void startIntents(PendingIntent pendingIntent1, Intent fillInIntent1,
+ @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+ PendingIntent pendingIntent2, Intent fillInIntent2,
+ @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
+ @SplitPosition int splitPosition, float splitRatio,
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (!mMainStage.isActive()) {
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(wct, false /* reparent */);
+ }
+
+ prepareEvictChildTasksIfSplitActive(wct);
+ mSplitLayout.setDivideRatio(splitRatio);
+ updateWindowBounds(mSplitLayout, wct);
+ wct.reorder(mRootTaskInfo.token, true);
+ setRootForceTranslucent(false, wct);
+
+ setSideStagePosition(splitPosition, wct);
+ options1 = options1 != null ? options1 : new Bundle();
+ addActivityOptions(options1, mSideStage);
+ if (shortcutInfo1 != null) {
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
+ } else {
+ wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+ }
+ options2 = options2 != null ? options2 : new Bundle();
+ addActivityOptions(options2, mMainStage);
+ if (shortcutInfo2 != null) {
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo2, options2);
+ } else {
+ wct.sendPendingIntent(pendingIntent2, fillInIntent2, options2);
+ }
+
+ mSplitTransitions.startEnterTransition(
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null, null);
+ setEnterInstanceId(instanceId);
+ }
+
/** Starts a pair of tasks using legacy transition. */
void startTasksWithLegacyTransition(int taskId1, @Nullable Bundle options1,
int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition,
@@ -895,10 +935,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue.queue(wrapAsSplitRemoteAnimation(adapter), WindowManager.TRANSIT_OPEN, wct);
}
- mSyncQueue.runInSync(t -> {
- setDividerVisibility(true, t);
- });
-
setEnterInstanceId(instanceId);
}
@@ -937,6 +973,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
onRemoteAnimationFinishedOrCancelled(evictWct);
+ setDividerVisibility(true, null);
try {
adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
@@ -977,6 +1014,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
t.setPosition(apps[i].leash, 0, 0);
}
}
+ setDividerVisibility(true, t);
t.apply();
IRemoteAnimationFinishedCallback wrapCallback =
@@ -1467,6 +1505,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void finishEnterSplitScreen(SurfaceControl.Transaction t) {
mSplitLayout.init();
setDividerVisibility(true, t);
+ // Ensure divider surface are re-parented back into the hierarchy at the end of the
+ // transition. See Transition#buildFinishTransaction for more detail.
+ t.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash);
+
updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
t.show(mRootTaskLeash);
setSplitsVisible(true);
@@ -1776,6 +1818,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setDividerVisibility(mainStageVisible, null);
}
+ // Set divider visibility flag and try to apply it, the param transaction is used to apply.
+ // See applyDividerVisibility for more detail.
private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) {
if (visible == mDividerVisible) {
return;
@@ -1802,14 +1846,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return;
}
- if (t != null) {
- applyDividerVisibility(t);
- } else {
- mSyncQueue.runInSync(transaction -> applyDividerVisibility(transaction));
- }
+ applyDividerVisibility(t);
}
- private void applyDividerVisibility(SurfaceControl.Transaction t) {
+ // Apply divider visibility by current visibility flag. If param transaction is non-null, it
+ // will apply by that transaction, if it is null and visible, it will run a fade-in animation,
+ // otherwise hide immediately.
+ private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
@@ -1826,7 +1869,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDividerFadeInAnimator.cancel();
}
- if (mDividerVisible) {
+ mSplitLayout.getRefDividerBounds(mTempRect1);
+ if (t != null) {
+ t.setVisibility(dividerLeash, mDividerVisible);
+ t.setLayer(dividerLeash, Integer.MAX_VALUE);
+ t.setPosition(dividerLeash, mTempRect1.left, mTempRect1.top);
+ } else if (mDividerVisible) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
mDividerFadeInAnimator = ValueAnimator.ofFloat(0f, 1f);
mDividerFadeInAnimator.addUpdateListener(animation -> {
@@ -1866,7 +1914,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDividerFadeInAnimator.start();
} else {
- t.hide(dividerLeash);
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ transaction.hide(dividerLeash);
+ transaction.apply();
+ mTransactionPool.release(transaction);
}
}
@@ -2450,7 +2501,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
finishEnterSplitScreen(finishT);
- addDividerBarToTransition(info, finishT, true /* show */);
+ addDividerBarToTransition(info, true /* show */);
return true;
}
@@ -2593,7 +2644,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
// TODO: Have a proper remote for this. Until then, though, reset state and use the
// normal animation stuff (which falls back to the normal launcher remote).
- t.hide(mSplitLayout.getDividerLeash());
+ setDividerVisibility(false, t);
mSplitLayout.release(t);
mSplitTransitions.mPendingDismiss = null;
return false;
@@ -2611,7 +2662,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
});
}
- addDividerBarToTransition(info, finishT, false /* show */);
+ addDividerBarToTransition(info, false /* show */);
return true;
}
@@ -2652,11 +2703,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
logExit(EXIT_REASON_UNKNOWN);
}
- private void addDividerBarToTransition(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction finishT, boolean show) {
+ private void addDividerBarToTransition(@NonNull TransitionInfo info, boolean show) {
final SurfaceControl leash = mSplitLayout.getDividerLeash();
final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash);
mSplitLayout.getRefDividerBounds(mTempRect1);
+ barChange.setParent(mRootTaskInfo.token);
barChange.setStartAbsBounds(mTempRect1);
barChange.setEndAbsBounds(mTempRect1);
barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK);
@@ -2664,15 +2715,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Technically this should be order-0, but this is running after layer assignment
// and it's a special case, so just add to end.
info.addChange(barChange);
-
- if (show) {
- finishT.setLayer(leash, Integer.MAX_VALUE);
- finishT.setPosition(leash, mTempRect1.left, mTempRect1.top);
- finishT.show(leash);
- // Ensure divider surface are re-parented back into the hierarchy at the end of the
- // transition. See Transition#buildFinishTransaction for more detail.
- finishT.reparent(leash, mRootTaskLeash);
- }
}
RemoteAnimationTarget getDividerBarLegacyTarget() {
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 fa4de16b37f1..bdb7d44bad32 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
@@ -143,6 +143,10 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Transition type to fullscreen from desktop mode. */
public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
+ /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */
+ public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
+ WindowManager.TRANSIT_FIRST_CUSTOM + 13;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index afc573e4fcbb..5226eeebcaa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -34,6 +34,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Handler;
@@ -557,8 +558,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragToDesktopAnimationStarted = false;
return;
} else if (mDragToDesktopAnimationStarted) {
- mDesktopTasksController.ifPresent(c ->
- c.moveToFullscreen(relevantDecor.mTaskInfo));
+ Point startPosition = new Point((int) ev.getX(), (int) ev.getY());
+ mDesktopTasksController.ifPresent(
+ c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo,
+ startPosition));
mDragToDesktopAnimationStarted = false;
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index a004e37c6345..67e99d73b811 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -42,6 +42,7 @@ import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
+import android.window.SurfaceSyncGroup;
import android.window.WindowContainerTransaction;
import com.android.launcher3.icons.IconProvider;
@@ -311,51 +312,50 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Create and display handle menu window
*/
void createHandleMenu() {
+ final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
updateHandleMenuPillPositions();
- createAppInfoPill(t);
+ createAppInfoPill(t, ssg);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
if (shouldShowWindowingPill) {
- createWindowingPill(t);
+ createWindowingPill(t, ssg);
}
- createMoreActionsPill(t);
+ createMoreActionsPill(t, ssg);
- mSyncQueue.runInSync(transaction -> {
- transaction.merge(t);
- t.close();
- });
+ ssg.addTransaction(t);
+ ssg.markSyncReady();
setupHandleMenu(shouldShowWindowingPill);
}
- private void createAppInfoPill(SurfaceControl.Transaction t) {
+ private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mHandleMenuAppInfoPillPosition.x;
final int y = (int) mHandleMenuAppInfoPillPosition.y;
mHandleMenuAppInfoPill = addWindow(
R.layout.desktop_mode_window_decor_handle_menu_app_info_pill,
"Menu's app info pill",
- t, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius);
+ t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius);
}
- private void createWindowingPill(SurfaceControl.Transaction t) {
+ private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mHandleMenuWindowingPillPosition.x;
final int y = (int) mHandleMenuWindowingPillPosition.y;
mHandleMenuWindowingPill = addWindow(
R.layout.desktop_mode_window_decor_handle_menu_windowing_pill,
"Menu's windowing pill",
- t, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius);
+ t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius);
}
- private void createMoreActionsPill(SurfaceControl.Transaction t) {
+ private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mHandleMenuMoreActionsPillPosition.x;
final int y = (int) mHandleMenuMoreActionsPillPosition.y;
mHandleMenuMoreActionsPill = addWindow(
R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill,
"Menu's more actions pill",
- t, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius);
+ t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius);
}
private void setupHandleMenu(boolean windowingPillShown) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index bc5fd4dcbdc8..19a31822aabb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -34,6 +34,7 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
+import android.window.SurfaceSyncGroup;
import android.window.TaskConstants;
import android.window.WindowContainerTransaction;
@@ -192,13 +193,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
if (params.mLayoutResId != 0) {
outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
- .inflate(params.mLayoutResId, null);
+ .inflate(params.mLayoutResId, null);
}
}
if (outResult.mRootView == null) {
outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
- .inflate(params.mLayoutResId , null);
+ .inflate(params.mLayoutResId, null);
}
// DecorationContainerSurface
@@ -382,18 +383,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
/**
* Create a window associated with this WindowDecoration.
* Note that subclass must dispose of this when the task is hidden/closed.
- * @param layoutId layout to make the window from
- * @param t the transaction to apply
- * @param xPos x position of new window
- * @param yPos y position of new window
- * @param width width of new window
- * @param height height of new window
+ *
+ * @param layoutId layout to make the window from
+ * @param t the transaction to apply
+ * @param xPos x position of new window
+ * @param yPos y position of new window
+ * @param width width of new window
+ * @param height height of new window
* @param shadowRadius radius of the shadow of the new window
* @param cornerRadius radius of the corners of the new window
* @return the {@link AdditionalWindow} that was added.
*/
AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t,
- int xPos, int yPos, int width, int height, int shadowRadius, int cornerRadius) {
+ SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height, int shadowRadius,
+ int cornerRadius) {
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
SurfaceControl windowSurfaceControl = builder
.setName(namePrefix + " of Task=" + mTaskInfo.taskId)
@@ -417,12 +420,12 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
windowSurfaceControl, null /* hostInputToken */);
SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory
.create(mDecorWindowContext, mDisplay, windowManager);
- viewHost.setView(v, lp);
+ ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp));
return new AdditionalWindow(windowSurfaceControl, viewHost,
mSurfaceControlTransactionSupplier);
}
- static class RelayoutParams{
+ static class RelayoutParams {
RunningTaskInfo mRunningTaskInfo;
int mLayoutResId;
int mCaptionHeightId;
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 67ca9a1a17f7..b6d92814ad43 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -29,6 +29,7 @@
<option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
<option name="teardown-command" value="settings delete system show_touches" />
<option name="teardown-command" value="settings delete system pointer_location" />
+ <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 806bffebd4cb..d95c7a488ea1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -166,6 +166,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 0);
mController.setTriggerBack(true);
+ }
+
+ private void releaseBackGesture() {
doMotionEvent(MotionEvent.ACTION_UP, 0);
}
@@ -201,6 +204,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
.setOnBackNavigationDone(new RemoteCallback(result)));
triggerBackGesture();
simulateRemoteAnimationStart(type);
+ mShellExecutor.flushAll();
+ releaseBackGesture();
simulateRemoteAnimationFinished();
mShellExecutor.flushAll();
@@ -252,6 +257,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, false);
triggerBackGesture();
+ releaseBackGesture();
verify(mAppCallback, times(1)).onBackInvoked();
@@ -269,6 +275,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
triggerBackGesture();
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ releaseBackGesture();
+
// Check that back invocation is dispatched.
verify(mAnimatorCallback).onBackInvoked();
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
@@ -308,6 +316,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
triggerBackGesture();
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
+ mShellExecutor.flushAll();
+
+ releaseBackGesture();
// Simulate transition timeout.
mShellExecutor.flushAll();
@@ -369,6 +380,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
simulateRemoteAnimationStart(type);
mShellExecutor.flushAll();
+ releaseBackGesture();
+ mShellExecutor.flushAll();
+
assertTrue("Navigation Done callback not called for "
+ BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
assertTrue("TriggerBack should have been true", result.mTriggerBack);
@@ -395,6 +409,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
.setOnBackNavigationDone(new RemoteCallback(result)));
triggerBackGesture();
mShellExecutor.flushAll();
+ releaseBackGesture();
+ mShellExecutor.flushAll();
assertTrue("Navigation Done callback not called for "
+ BackNavigationInfo.typeToString(type), result.mBackNavigationDone);
@@ -458,9 +474,12 @@ public class BackAnimationControllerTest extends ShellTestCase {
private void doMotionEvent(int actionDown, int coordinate) {
mController.onMotionEvent(
- coordinate, coordinate,
- actionDown,
- BackEvent.EDGE_LEFT);
+ /* touchX */ coordinate,
+ /* touchY */ coordinate,
+ /* velocityX = */ 0,
+ /* velocityY = */ 0,
+ /* keyAction */ actionDown,
+ /* swipeEdge */ BackEvent.EDGE_LEFT);
}
private void simulateRemoteAnimationStart(int type) throws RemoteException {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 3608474bd90e..874ef80c29f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.back;
-import static android.window.BackEvent.EDGE_LEFT;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -48,12 +46,21 @@ public class BackProgressAnimatorTest {
private CountDownLatch mTargetProgressCalled = new CountDownLatch(1);
private Handler mMainThreadHandler;
+ private BackMotionEvent backMotionEventFrom(float touchX, float progress) {
+ return new BackMotionEvent(
+ /* touchX = */ touchX,
+ /* touchY = */ 0,
+ /* progress = */ progress,
+ /* velocityX = */ 0,
+ /* velocityY = */ 0,
+ /* swipeEdge = */ BackEvent.EDGE_LEFT,
+ /* departingAnimationTarget = */ null);
+ }
+
@Before
public void setUp() throws Exception {
mMainThreadHandler = new Handler(Looper.getMainLooper());
- final BackMotionEvent backEvent = new BackMotionEvent(
- 0, 0,
- 0, EDGE_LEFT, null);
+ final BackMotionEvent backEvent = backMotionEventFrom(0, 0);
mMainThreadHandler.post(
() -> {
mProgressAnimator = new BackProgressAnimator();
@@ -63,9 +70,7 @@ public class BackProgressAnimatorTest {
@Test
public void testBackProgressed() throws InterruptedException {
- final BackMotionEvent backEvent = new BackMotionEvent(
- 100, 0,
- mTargetProgress, EDGE_LEFT, null);
+ final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress);
mMainThreadHandler.post(
() -> mProgressAnimator.onBackProgressed(backEvent));
@@ -78,9 +83,7 @@ public class BackProgressAnimatorTest {
@Test
public void testBackCancelled() throws InterruptedException {
// Give the animator some progress.
- final BackMotionEvent backEvent = new BackMotionEvent(
- 100, 0,
- mTargetProgress, EDGE_LEFT, null);
+ final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress);
mMainThreadHandler.post(
() -> mProgressAnimator.onBackProgressed(backEvent));
mTargetProgressCalled.await(1, TimeUnit.SECONDS);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
index ba9c159bad28..d62e6601723a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
@@ -47,43 +47,45 @@ public class TouchTrackerTest {
public void generatesProgress_leftEdge() {
mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
float touchX = 10;
+ float velocityX = 0;
+ float velocityY = 0;
// Pre-commit
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
// Post-commit
touchX += 100;
mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
// Cancel
touchX -= 10;
mTouchTracker.setTriggerBack(false);
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), 0, 0f);
// Cancel more
touchX -= 10;
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), 0, 0f);
// Restart
touchX += 10;
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), 0, 0f);
// Restarted, but pre-commit
float restartX = touchX;
touchX += 10;
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f);
// Restarted, post-commit
touchX += 10;
mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
}
@@ -91,43 +93,45 @@ public class TouchTrackerTest {
public void generatesProgress_rightEdge() {
mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT);
float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge
+ float velocityX = 0f;
+ float velocityY = 0f;
// Pre-commit
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
// Post-commit
touchX -= 100;
mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
// Cancel
touchX += 10;
mTouchTracker.setTriggerBack(false);
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), 0, 0f);
// Cancel more
touchX += 10;
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), 0, 0f);
// Restart
touchX -= 10;
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), 0, 0f);
// Restarted, but pre-commit
float restartX = touchX;
touchX -= 10;
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f);
// Restarted, post-commit
touchX -= 10;
mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0);
+ mTouchTracker.update(touchX, 0, velocityX, velocityY);
assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 15bb10ed4f2b..842c699fa42d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -262,7 +262,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
DisplayLayout layout = new DisplayLayout(info,
mContext.getResources(), true, true);
mPipDisplayLayoutState.setDisplayLayout(layout);
- mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+ doReturn(PipAnimationController.ANIM_TYPE_ALPHA).when(mMockPipAnimationController)
+ .takeOneShotEnterAnimationType();
mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index eda6fdc4dbd4..e6219d1aa792 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -113,6 +113,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private SurfaceSession mSurfaceSession = new SurfaceSession();
private SurfaceControl mRootLeash;
+ private SurfaceControl mDividerLeash;
private ActivityManager.RunningTaskInfo mRootTask;
private StageCoordinator mStageCoordinator;
private Transitions mTransitions;
@@ -129,12 +130,14 @@ public class StageCoordinatorTests extends ShellTestCase {
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, Optional.empty()));
+ mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build();
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
when(mSplitLayout.isLandscape()).thenReturn(false);
when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true);
+ when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index e8147ff264cc..38a519af934b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -49,6 +49,7 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager.LayoutParams;
+import android.window.SurfaceSyncGroup;
import android.window.TaskConstants;
import android.window.WindowContainerTransaction;
@@ -100,6 +101,8 @@ public class WindowDecorationTests extends ShellTestCase {
private TestView mMockView;
@Mock
private WindowContainerTransaction mMockWindowContainerTransaction;
+ @Mock
+ private SurfaceSyncGroup mMockSurfaceSyncGroup;
private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions =
new ArrayList<>();
@@ -553,7 +556,7 @@ public class WindowDecorationTests extends ShellTestCase {
String name = "Test Window";
WindowDecoration.AdditionalWindow additionalWindow =
addWindow(R.layout.desktop_mode_window_decor_handle_menu_app_info_pill, name,
- mMockSurfaceControlAddWindowT, x, y,
+ mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y,
width, height, shadowRadius, cornerRadius);
return additionalWindow;
}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 23611efccd73..7e9d44fbdbd1 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -117,12 +117,8 @@ void CacheManager::trimMemory(TrimLevel mode) {
// flush and submit all work to the gpu and wait for it to finish
mGrContext->flushAndSubmit(/*syncCpu=*/true);
- if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) {
- mode = TrimLevel::COMPLETE;
- }
-
switch (mode) {
- case TrimLevel::COMPLETE:
+ case TrimLevel::BACKGROUND:
mGrContext->freeGpuResources();
SkGraphics::PurgeAllCaches();
mRenderThread.destroyRenderingContext();
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 1a3e54d54ee7..afa0a3271906 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -391,8 +391,26 @@ public final class SoundTriggerDetector {
* @hide
*/
@Override
- public void onError(int status) {
- Slog.d(TAG, "onError()" + status);
+ public void onRecognitionPaused() {
+ Slog.d(TAG, "onRecognitionPaused()");
+ mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onRecognitionResumed() {
+ Slog.d(TAG, "onRecognitionResumed()");
+ mHandler.sendEmptyMessage(MSG_DETECTION_RESUME);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onPreempted() {
+ Slog.d(TAG, "onPreempted()");
mHandler.sendEmptyMessage(MSG_DETECTION_ERROR);
}
@@ -400,18 +418,27 @@ public final class SoundTriggerDetector {
* @hide
*/
@Override
- public void onRecognitionPaused() {
- Slog.d(TAG, "onRecognitionPaused()");
- mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE);
+ public void onModuleDied() {
+ Slog.d(TAG, "onModuleDied()");
+ mHandler.sendEmptyMessage(MSG_DETECTION_ERROR);
}
/**
* @hide
*/
@Override
- public void onRecognitionResumed() {
- Slog.d(TAG, "onRecognitionResumed()");
- mHandler.sendEmptyMessage(MSG_DETECTION_RESUME);
+ public void onResumeFailed(int status) {
+ Slog.d(TAG, "onResumeFailed()" + status);
+ mHandler.sendEmptyMessage(MSG_DETECTION_ERROR);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onPauseFailed(int status) {
+ Slog.d(TAG, "onPauseFailed()" + status);
+ mHandler.sendEmptyMessage(MSG_DETECTION_ERROR);
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index e53e956c317c..a9bee039264e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -107,7 +107,7 @@ class CredentialManagerRepo(
initialUiState = when (requestInfo?.type) {
RequestInfo.TYPE_CREATE -> {
- val defaultProviderId = userConfigRepo.getDefaultProviderId()
+ val defaultProviderIdSetByUser = userConfigRepo.getDefaultProviderId()
val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
val providerEnableListUiState = getCreateProviderEnableListInitialUiState()
val providerDisableListUiState = getCreateProviderDisableListInitialUiState()
@@ -115,12 +115,14 @@ class CredentialManagerRepo(
getCreateRequestDisplayInfoInitialUiState(originName)!!
UiState(
createCredentialUiState = CreateFlowUtils.toCreateCredentialUiState(
- providerEnableListUiState,
- providerDisableListUiState,
- defaultProviderId,
- requestDisplayInfoUiState,
+ enabledProviders = providerEnableListUiState,
+ disabledProviders = providerDisableListUiState,
+ defaultProviderIdPreferredByApp =
+ requestDisplayInfoUiState.appPreferredDefaultProviderId,
+ defaultProviderIdSetByUser = defaultProviderIdSetByUser,
+ requestDisplayInfo = requestDisplayInfoUiState,
isOnPasskeyIntroStateAlready = false,
- isPasskeyFirstUse
+ isPasskeyFirstUse = isPasskeyFirstUse,
)!!,
getCredentialUiState = null,
cancelRequestState = cancelUiRequestState
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 29ec970966d6..4d2bb4c6016a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -250,9 +250,15 @@ class CredentialSelectorViewModel(
return
}
val newUiState = CreateFlowUtils.toCreateCredentialUiState(
- prevUiState.enabledProviders, prevUiState.disabledProviders,
- userConfigRepo.getDefaultProviderId(), prevUiState.requestDisplayInfo, true,
- userConfigRepo.getIsPasskeyFirstUse())
+ enabledProviders = prevUiState.enabledProviders,
+ disabledProviders = prevUiState.disabledProviders,
+ defaultProviderIdPreferredByApp =
+ prevUiState.requestDisplayInfo.appPreferredDefaultProviderId,
+ defaultProviderIdSetByUser = userConfigRepo.getDefaultProviderId(),
+ requestDisplayInfo = prevUiState.requestDisplayInfo,
+ isOnPasskeyIntroStateAlready = true,
+ isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
+ )
if (newUiState == null) {
Log.d(Constants.LOG_TAG, "Unable to update create ui state")
onInternalError()
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index ca891294576b..e8d3b1f45be3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -252,13 +252,6 @@ class GetFlowUtils {
))
}
is PublicKeyCredentialEntry -> {
- val passkeyUsername = credentialEntry.username.toString()
- val passkeyDisplayName = credentialEntry.displayName?.toString() ?: ""
- val (username, displayName) = userAndDisplayNameForPasskey(
- passkeyUsername = passkeyUsername,
- passkeyDisplayName = passkeyDisplayName,
- )
-
result.add(CredentialEntryInfo(
providerId = providerId,
providerDisplayName = providerLabel,
@@ -268,8 +261,8 @@ class GetFlowUtils {
fillInIntent = it.frameworkExtrasIntent,
credentialType = CredentialType.PASSKEY,
credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
- userName = username,
- displayName = displayName,
+ userName = credentialEntry.username.toString(),
+ displayName = credentialEntry.displayName?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
shouldTintIcon = credentialEntry.isDefaultIcon,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
@@ -471,6 +464,9 @@ class CreateFlowUtils {
createCredentialRequest.candidateQueryData,
createCredentialRequest.isSystemProviderRequired
)
+ val appPreferredDefaultProviderId: String? =
+ if (!requestInfo.hasPermissionToOverrideDefault()) null
+ else createCredentialRequestJetpack?.displayInfo?.preferDefaultProvider
return when (createCredentialRequestJetpack) {
is CreatePasswordRequest -> RequestDisplayInfo(
createCredentialRequestJetpack.id,
@@ -479,6 +475,7 @@ class CreateFlowUtils {
appLabel,
context.getDrawable(R.drawable.ic_password_24) ?: return null,
preferImmediatelyAvailableCredentials = false,
+ appPreferredDefaultProviderId = appPreferredDefaultProviderId,
)
is CreatePublicKeyCredentialRequest -> {
newRequestDisplayInfoFromPasskeyJson(
@@ -487,6 +484,7 @@ class CreateFlowUtils {
context = context,
preferImmediatelyAvailableCredentials =
createCredentialRequestJetpack.preferImmediatelyAvailableCredentials,
+ appPreferredDefaultProviderId = appPreferredDefaultProviderId,
)
}
is CreateCustomCredentialRequest -> {
@@ -502,6 +500,7 @@ class CreateFlowUtils {
typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context)
?: context.getDrawable(R.drawable.ic_other_sign_in_24) ?: return null,
preferImmediatelyAvailableCredentials = false,
+ appPreferredDefaultProviderId = appPreferredDefaultProviderId,
)
}
else -> null
@@ -511,20 +510,27 @@ class CreateFlowUtils {
fun toCreateCredentialUiState(
enabledProviders: List<EnabledProviderInfo>,
disabledProviders: List<DisabledProviderInfo>?,
- defaultProviderId: String?,
+ defaultProviderIdPreferredByApp: String?,
+ defaultProviderIdSetByUser: String?,
requestDisplayInfo: RequestDisplayInfo,
isOnPasskeyIntroStateAlready: Boolean,
isPasskeyFirstUse: Boolean,
): CreateCredentialUiState? {
var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
var remoteEntry: RemoteInfo? = null
- var defaultProvider: EnabledProviderInfo? = null
+ var defaultProviderPreferredByApp: EnabledProviderInfo? = null
+ var defaultProviderSetByUser: EnabledProviderInfo? = null
var createOptionsPairs:
MutableList<Pair<CreateOptionInfo, EnabledProviderInfo>> = mutableListOf()
enabledProviders.forEach { enabledProvider ->
- if (defaultProviderId != null) {
- if (enabledProvider.id == defaultProviderId) {
- defaultProvider = enabledProvider
+ if (defaultProviderIdPreferredByApp != null) {
+ if (enabledProvider.id == defaultProviderIdPreferredByApp) {
+ defaultProviderPreferredByApp = enabledProvider
+ }
+ }
+ if (defaultProviderIdSetByUser != null) {
+ if (enabledProvider.id == defaultProviderIdSetByUser) {
+ defaultProviderSetByUser = enabledProvider
}
}
if (enabledProvider.createOptions.isNotEmpty()) {
@@ -543,12 +549,14 @@ class CreateFlowUtils {
remoteEntry = currRemoteEntry
}
}
+ val defaultProvider = defaultProviderPreferredByApp ?: defaultProviderSetByUser
val initialScreenState = toCreateScreenState(
- /*createOptionSize=*/createOptionsPairs.size,
- /*isOnPasskeyIntroStateAlready=*/isOnPasskeyIntroStateAlready,
- /*requestDisplayInfo=*/requestDisplayInfo,
- /*defaultProvider=*/defaultProvider, /*remoteEntry=*/remoteEntry,
- /*isPasskeyFirstUse=*/isPasskeyFirstUse
+ createOptionSize = createOptionsPairs.size,
+ isOnPasskeyIntroStateAlready = isOnPasskeyIntroStateAlready,
+ requestDisplayInfo = requestDisplayInfo,
+ defaultProvider = defaultProvider,
+ remoteEntry = remoteEntry,
+ isPasskeyFirstUse = isPasskeyFirstUse
) ?: return null
return CreateCredentialUiState(
enabledProviders = enabledProviders,
@@ -674,6 +682,7 @@ class CreateFlowUtils {
appLabel: String,
context: Context,
preferImmediatelyAvailableCredentials: Boolean,
+ appPreferredDefaultProviderId: String?,
): RequestDisplayInfo? {
val json = JSONObject(requestJson)
var passkeyUsername = ""
@@ -694,6 +703,7 @@ class CreateFlowUtils {
appLabel,
context.getDrawable(R.drawable.ic_passkey_24) ?: return null,
preferImmediatelyAvailableCredentials,
+ appPreferredDefaultProviderId,
)
}
}
@@ -707,7 +717,7 @@ class CreateFlowUtils {
* 2) username on top if display-name is not available.
* 3) don't show username on second line if username == display-name
*/
-private fun userAndDisplayNameForPasskey(
+fun userAndDisplayNameForPasskey(
passkeyUsername: String,
passkeyDisplayName: String,
): Pair<String, String> {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
index 14bf4f23384b..2df0c7a9b1e8 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SectionHeader.kt
@@ -27,8 +27,12 @@ import androidx.compose.ui.unit.dp
import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
@Composable
-fun CredentialListSectionHeader(text: String) {
- InternalSectionHeader(text, LocalAndroidColorScheme.current.colorOnSurfaceVariant)
+fun CredentialListSectionHeader(text: String, isFirstSection: Boolean) {
+ InternalSectionHeader(
+ text = text,
+ color = LocalAndroidColorScheme.current.colorOnSurfaceVariant,
+ applyTopPadding = !isFirstSection
+ )
}
@Composable
@@ -37,8 +41,10 @@ fun MoreAboutPasskeySectionHeader(text: String) {
}
@Composable
-private fun InternalSectionHeader(text: String, color: Color) {
- Row(modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(top = 8.dp)) {
+private fun InternalSectionHeader(text: String, color: Color, applyTopPadding: Boolean = false) {
+ Row(modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(
+ top = if (applyTopPadding) 8.dp else 0.dp
+ )) {
SectionHeaderText(
text,
modifier = Modifier.padding(top = 20.dp, bottom = 8.dp),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 66d7db896247..96010cc66821 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -435,7 +435,7 @@ fun MoreOptionsRowIntroCard(
}
SheetContainerCard {
item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) }
- item { Divider(thickness = 24.dp, color = Color.Transparent) }
+ item { Divider(thickness = 16.dp, color = Color.Transparent) }
item {
HeadlineText(
text = stringResource(
@@ -633,6 +633,7 @@ fun MoreAboutPasskeysIntroCard(
}
}
item {
+ Divider(thickness = 8.dp, color = Color.Transparent)
MoreAboutPasskeySectionHeader(
text = stringResource(R.string.public_key_cryptography_title)
)
@@ -641,6 +642,7 @@ fun MoreAboutPasskeysIntroCard(
}
}
item {
+ Divider(thickness = 8.dp, color = Color.Transparent)
MoreAboutPasskeySectionHeader(
text = stringResource(R.string.improved_account_security_title)
)
@@ -649,6 +651,7 @@ fun MoreAboutPasskeysIntroCard(
}
}
item {
+ Divider(thickness = 8.dp, color = Color.Transparent)
MoreAboutPasskeySectionHeader(
text = stringResource(R.string.seamless_transition_title)
)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 4332fb34ce79..12bb6298b282 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -107,6 +107,7 @@ data class RequestDisplayInfo(
val appName: String,
val typeIcon: Drawable,
val preferImmediatelyAvailableCredentials: Boolean,
+ val appPreferredDefaultProviderId: String?,
)
/**
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index c1ea1d8d1746..98bd020b234b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -66,6 +66,7 @@ import com.android.credentialmanager.common.ui.HeadlineIcon
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.Snackbar
import com.android.credentialmanager.logging.GetCredentialEvent
+import com.android.credentialmanager.userAndDisplayNameForPasskey
import com.android.internal.logging.UiEventLogger.UiEventEnum
@Composable
@@ -322,12 +323,15 @@ fun AllSignInOptionCard(
bottomPadding = 0.dp,
)
}) {
+ var isFirstSection = true
// For username
items(sortedUserNameToCredentialEntryList) { item ->
PerUserNameCredentials(
perUserNameCredentialEntryList = item,
onEntrySelected = onEntrySelected,
+ isFirstSection = isFirstSection,
)
+ isFirstSection = false
}
// Locked password manager
if (authenticationEntryList.isNotEmpty()) {
@@ -335,7 +339,9 @@ fun AllSignInOptionCard(
LockedCredentials(
authenticationEntryList = authenticationEntryList,
onEntrySelected = onEntrySelected,
+ isFirstSection = isFirstSection,
)
+ isFirstSection = false
}
}
// From another device
@@ -345,15 +351,19 @@ fun AllSignInOptionCard(
RemoteEntryCard(
remoteEntry = remoteEntry,
onEntrySelected = onEntrySelected,
+ isFirstSection = isFirstSection,
)
+ isFirstSection = false
}
}
// Manage sign-ins (action chips)
item {
ActionChips(
providerInfoList = providerInfoList,
- onEntrySelected = onEntrySelected
+ onEntrySelected = onEntrySelected,
+ isFirstSection = isFirstSection,
)
+ isFirstSection = false
}
}
onLog(GetCredentialEvent.CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD)
@@ -366,6 +376,7 @@ fun AllSignInOptionCard(
fun ActionChips(
providerInfoList: List<ProviderInfo>,
onEntrySelected: (BaseEntry) -> Unit,
+ isFirstSection: Boolean,
) {
val actionChips = providerInfoList.flatMap { it.actionEntryList }
if (actionChips.isEmpty()) {
@@ -373,7 +384,8 @@ fun ActionChips(
}
CredentialListSectionHeader(
- text = stringResource(R.string.get_dialog_heading_manage_sign_ins)
+ text = stringResource(R.string.get_dialog_heading_manage_sign_ins),
+ isFirstSection = isFirstSection,
)
CredentialContainerCard {
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
@@ -388,9 +400,11 @@ fun ActionChips(
fun RemoteEntryCard(
remoteEntry: RemoteEntryInfo,
onEntrySelected: (BaseEntry) -> Unit,
+ isFirstSection: Boolean,
) {
CredentialListSectionHeader(
- text = stringResource(R.string.get_dialog_heading_from_another_device)
+ text = stringResource(R.string.get_dialog_heading_from_another_device),
+ isFirstSection = isFirstSection,
)
CredentialContainerCard {
Column(
@@ -412,9 +426,11 @@ fun RemoteEntryCard(
fun LockedCredentials(
authenticationEntryList: List<AuthenticationEntryInfo>,
onEntrySelected: (BaseEntry) -> Unit,
+ isFirstSection: Boolean,
) {
CredentialListSectionHeader(
- text = stringResource(R.string.get_dialog_heading_locked_password_managers)
+ text = stringResource(R.string.get_dialog_heading_locked_password_managers),
+ isFirstSection = isFirstSection,
)
CredentialContainerCard {
Column(
@@ -432,11 +448,13 @@ fun LockedCredentials(
fun PerUserNameCredentials(
perUserNameCredentialEntryList: PerUserNameCredentialEntryList,
onEntrySelected: (BaseEntry) -> Unit,
+ isFirstSection: Boolean,
) {
CredentialListSectionHeader(
text = stringResource(
R.string.get_dialog_heading_for_username, perUserNameCredentialEntryList.userName
- )
+ ),
+ isFirstSection = isFirstSection,
)
CredentialContainerCard {
Column(
@@ -457,6 +475,10 @@ fun CredentialEntryRow(
enforceOneLine: Boolean = false,
onTextLayout: (TextLayoutResult) -> Unit = {},
) {
+ val (username, displayName) = if (credentialEntryInfo.credentialType == CredentialType.PASSKEY)
+ userAndDisplayNameForPasskey(
+ credentialEntryInfo.userName, credentialEntryInfo.displayName ?: "")
+ else Pair(credentialEntryInfo.userName, credentialEntryInfo.displayName)
Entry(
onClick = { onEntrySelected(credentialEntryInfo) },
iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(),
@@ -465,13 +487,13 @@ fun CredentialEntryRow(
iconPainter =
if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
else null,
- entryHeadlineText = credentialEntryInfo.userName,
+ entryHeadlineText = username,
entrySecondLineText = if (
credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
"••••••••••••"
} else {
val itemsToDisplay = listOf(
- credentialEntryInfo.displayName,
+ displayName,
credentialEntryInfo.credentialTypeDisplayName,
credentialEntryInfo.providerDisplayName
).filterNot(TextUtils::isEmpty)
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 8cda37665035..bf24c86b8d8b 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -59,32 +59,36 @@ public class FooterPreference extends Preference {
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
TextView title = holder.itemView.findViewById(android.R.id.title);
- if (!TextUtils.isEmpty(mContentDescription)) {
+ if (title != null && !TextUtils.isEmpty(mContentDescription)) {
title.setContentDescription(mContentDescription);
}
TextView learnMore = holder.itemView.findViewById(R.id.settingslib_learn_more);
- if (learnMore != null && mLearnMoreListener != null) {
- learnMore.setVisibility(View.VISIBLE);
- if (TextUtils.isEmpty(mLearnMoreText)) {
- mLearnMoreText = learnMore.getText();
+ if (learnMore != null) {
+ if (mLearnMoreListener != null) {
+ learnMore.setVisibility(View.VISIBLE);
+ if (TextUtils.isEmpty(mLearnMoreText)) {
+ mLearnMoreText = learnMore.getText();
+ } else {
+ learnMore.setText(mLearnMoreText);
+ }
+ SpannableString learnMoreText = new SpannableString(mLearnMoreText);
+ if (mLearnMoreSpan != null) {
+ learnMoreText.removeSpan(mLearnMoreSpan);
+ }
+ mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener);
+ learnMoreText.setSpan(mLearnMoreSpan, 0,
+ learnMoreText.length(), 0);
+ learnMore.setText(learnMoreText);
} else {
- learnMore.setText(mLearnMoreText);
+ learnMore.setVisibility(View.GONE);
}
- SpannableString learnMoreText = new SpannableString(mLearnMoreText);
- if (mLearnMoreSpan != null) {
- learnMoreText.removeSpan(mLearnMoreSpan);
- }
- mLearnMoreSpan = new FooterLearnMoreSpan(mLearnMoreListener);
- learnMoreText.setSpan(mLearnMoreSpan, 0,
- learnMoreText.length(), 0);
- learnMore.setText(learnMoreText);
- } else {
- learnMore.setVisibility(View.GONE);
}
View icon = holder.itemView.findViewById(R.id.icon_frame);
- icon.setVisibility(mIconVisibility);
+ if (icon != null) {
+ icon.setVisibility(mIconVisibility);
+ }
}
@Override
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index 5342def0003d..2c3e58ece8e0 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -20,9 +20,12 @@ import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.ResolveInfo
import com.android.internal.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -33,7 +36,11 @@ import kotlinx.coroutines.runBlocking
*/
internal interface AppListRepository {
/** Loads the list of [ApplicationInfo]. */
- suspend fun loadApps(userId: Int, showInstantApps: Boolean): List<ApplicationInfo>
+ suspend fun loadApps(
+ userId: Int,
+ showInstantApps: Boolean = false,
+ matchAnyUserForAdmin: Boolean = false,
+ ): List<ApplicationInfo>
/** Gets the flow of predicate that could used to filter system app. */
fun showSystemPredicate(
@@ -61,10 +68,12 @@ object AppListRepositoryUtil {
internal class AppListRepositoryImpl(private val context: Context) : AppListRepository {
private val packageManager = context.packageManager
+ private val userManager = context.userManager
override suspend fun loadApps(
userId: Int,
showInstantApps: Boolean,
+ matchAnyUserForAdmin: Boolean,
): List<ApplicationInfo> = coroutineScope {
val hiddenSystemModulesDeferred = async {
packageManager.getInstalledModules(0)
@@ -75,12 +84,8 @@ internal class AppListRepositoryImpl(private val context: Context) : AppListRepo
val hideWhenDisabledPackagesDeferred = async {
context.resources.getStringArray(R.array.config_hideWhenDisabled_packageNames)
}
- val flags = PackageManager.ApplicationInfoFlags.of(
- (PackageManager.MATCH_DISABLED_COMPONENTS or
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
- )
val installedApplicationsAsUser =
- packageManager.getInstalledApplicationsAsUser(flags, userId)
+ getInstalledApplications(userId, matchAnyUserForAdmin)
val hiddenSystemModules = hiddenSystemModulesDeferred.await()
val hideWhenDisabledPackages = hideWhenDisabledPackagesDeferred.await()
@@ -89,6 +94,46 @@ internal class AppListRepositoryImpl(private val context: Context) : AppListRepo
}
}
+ private suspend fun getInstalledApplications(
+ userId: Int,
+ matchAnyUserForAdmin: Boolean,
+ ): List<ApplicationInfo> {
+ val regularFlags = ApplicationInfoFlags.of(
+ (PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
+ )
+ return if (!matchAnyUserForAdmin || !userManager.getUserInfo(userId).isAdmin) {
+ packageManager.getInstalledApplicationsAsUser(regularFlags, userId)
+ } else {
+ coroutineScope {
+ val deferredPackageNamesInChildProfiles =
+ userManager.getProfileIdsWithDisabled(userId)
+ .filter { it != userId }
+ .map {
+ async {
+ packageManager.getInstalledApplicationsAsUser(regularFlags, it)
+ .map { it.packageName }
+ }
+ }
+ val adminFlags = ApplicationInfoFlags.of(
+ PackageManager.MATCH_ANY_USER.toLong() or regularFlags.value
+ )
+ val allInstalledApplications =
+ packageManager.getInstalledApplicationsAsUser(adminFlags, userId)
+ val packageNamesInChildProfiles = deferredPackageNamesInChildProfiles
+ .awaitAll()
+ .flatten()
+ .toSet()
+ // If an app is for a child profile and not installed on the owner, not display as
+ // 'not installed for this user' in the owner. This will prevent duplicates of work
+ // only apps showing up in the personal profile.
+ allInstalledApplications.filter {
+ it.installed || it.packageName !in packageNamesInChildProfiles
+ }
+ }
+ }
+ }
+
override fun showSystemPredicate(
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
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
index 889604209b08..bd99ebdfa3e5 100644
--- 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
@@ -80,12 +80,15 @@ internal open class AppListViewModelImpl<T : AppRecord>(
private val scope = viewModelScope + Dispatchers.IO
private val userSubGraphsFlow = appListConfig.flow.map { config ->
- config.userIds.map { userId -> UserSubGraph(userId, config.showInstantApps) }
+ config.userIds.map { userId ->
+ UserSubGraph(userId, config.showInstantApps, config.matchAnyUserForAdmin)
+ }
}.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
private inner class UserSubGraph(
private val userId: Int,
private val showInstantApps: Boolean,
+ private val matchAnyUserForAdmin: Boolean,
) {
private val userIdFlow = flowOf(userId)
@@ -110,7 +113,8 @@ internal open class AppListViewModelImpl<T : AppRecord>(
fun reloadApps() {
scope.launch {
- appsStateFlow.value = appListRepository.loadApps(userId, showInstantApps)
+ appsStateFlow.value =
+ appListRepository.loadApps(userId, showInstantApps, matchAnyUserForAdmin)
}
}
}
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
index f4a6b59d4c4b..066db3475b36 100644
--- 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
@@ -62,6 +62,7 @@ private const val CONTENT_TYPE_HEADER = "header"
data class AppListConfig(
val userIds: List<Int>,
val showInstantApps: Boolean,
+ val matchAnyUserForAdmin: Boolean,
)
data class AppListState(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index 2ebbe8aab809..89bfa0eb646b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -38,6 +38,7 @@ fun <T : AppRecord> AppListPage(
title: String,
listModel: AppListModel<T>,
showInstantApps: Boolean = false,
+ matchAnyUserForAdmin: Boolean = false,
primaryUserOnly: Boolean = false,
noItemMessage: String? = null,
moreOptions: @Composable MoreOptionsScope.() -> Unit = {},
@@ -59,6 +60,7 @@ fun <T : AppRecord> AppListPage(
config = AppListConfig(
userIds = userGroup.userInfos.map { it.id },
showInstantApps = showInstantApps,
+ matchAnyUserForAdmin = matchAnyUserForAdmin,
),
listModel = listModel,
state = AppListState(
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index 57972edc6d3a..b732a6a4495e 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -23,9 +23,13 @@ import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.PackageManager.ResolveInfoFlags
import android.content.pm.ResolveInfo
+import android.content.pm.UserInfo
import android.content.res.Resources
+import android.os.UserManager
+import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -35,10 +39,13 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@@ -49,8 +56,8 @@ class AppListRepositoryTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
- @Mock
- private lateinit var context: Context
+ @Spy
+ private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var resources: Resources
@@ -58,6 +65,9 @@ class AppListRepositoryTest {
@Mock
private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var userManager: UserManager
+
private lateinit var repository: AppListRepository
@Before
@@ -66,36 +76,116 @@ class AppListRepositoryTest {
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(emptyArray())
whenever(context.packageManager).thenReturn(packageManager)
+ whenever(context.userManager).thenReturn(userManager)
whenever(packageManager.getInstalledModules(anyInt())).thenReturn(emptyList())
whenever(
- packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
+ packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), anyInt())
).thenReturn(emptyList())
+ whenever(userManager.getUserInfo(ADMIN_USER_ID)).thenReturn(UserInfo().apply {
+ flags = UserInfo.FLAG_ADMIN
+ })
+ whenever(userManager.getProfileIdsWithDisabled(ADMIN_USER_ID))
+ .thenReturn(intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID))
repository = AppListRepositoryImpl(context)
}
- private fun mockInstalledApplications(apps: List<ApplicationInfo>) {
+ private fun mockInstalledApplications(apps: List<ApplicationInfo>, userId: Int) {
whenever(
- packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(USER_ID))
+ packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId))
).thenReturn(apps)
}
@Test
fun loadApps_notShowInstantApps() = runTest {
- mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP))
+ mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP), ADMIN_USER_ID)
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
+ val appList = repository.loadApps(
+ userId = ADMIN_USER_ID,
+ showInstantApps = false,
+ )
- assertThat(appListFlow).containsExactly(NORMAL_APP)
+ assertThat(appList).containsExactly(NORMAL_APP)
}
@Test
fun loadApps_showInstantApps() = runTest {
- mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP))
+ mockInstalledApplications(listOf(NORMAL_APP, INSTANT_APP), ADMIN_USER_ID)
+
+ val appList = repository.loadApps(
+ userId = ADMIN_USER_ID,
+ showInstantApps = true,
+ )
+
+ assertThat(appList).containsExactly(NORMAL_APP, INSTANT_APP)
+ }
+
+ @Test
+ fun loadApps_notMatchAnyUserForAdmin_withRegularFlags() = runTest {
+ mockInstalledApplications(listOf(NORMAL_APP), ADMIN_USER_ID)
+
+ val appList = repository.loadApps(
+ userId = ADMIN_USER_ID,
+ matchAnyUserForAdmin = false,
+ )
+
+ assertThat(appList).containsExactly(NORMAL_APP)
+ val flags = ArgumentCaptor.forClass(ApplicationInfoFlags::class.java)
+ verify(packageManager).getInstalledApplicationsAsUser(flags.capture(), eq(ADMIN_USER_ID))
+ assertThat(flags.value.value).isEqualTo(
+ PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ )
+ }
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = true)
+ @Test
+ fun loadApps_matchAnyUserForAdmin_withMatchAnyUserFlag() = runTest {
+ mockInstalledApplications(listOf(NORMAL_APP), ADMIN_USER_ID)
+
+ val appList = repository.loadApps(
+ userId = ADMIN_USER_ID,
+ matchAnyUserForAdmin = true,
+ )
+
+ assertThat(appList).containsExactly(NORMAL_APP)
+ val flags = ArgumentCaptor.forClass(ApplicationInfoFlags::class.java)
+ verify(packageManager).getInstalledApplicationsAsUser(flags.capture(), eq(ADMIN_USER_ID))
+ assertThat(flags.value.value and PackageManager.MATCH_ANY_USER.toLong()).isGreaterThan(0L)
+ }
+
+ @Test
+ fun loadApps_matchAnyUserForAdminAndInstalledOnManagedProfileOnly_notDisplayed() = runTest {
+ val managedProfileOnlyPackageName = "installed.on.managed.profile.only"
+ mockInstalledApplications(listOf(ApplicationInfo().apply {
+ packageName = managedProfileOnlyPackageName
+ }), ADMIN_USER_ID)
+ mockInstalledApplications(listOf(ApplicationInfo().apply {
+ packageName = managedProfileOnlyPackageName
+ flags = ApplicationInfo.FLAG_INSTALLED
+ }), MANAGED_PROFILE_USER_ID)
+
+ val appList = repository.loadApps(
+ userId = ADMIN_USER_ID,
+ matchAnyUserForAdmin = true,
+ )
- assertThat(appListFlow).containsExactly(NORMAL_APP, INSTANT_APP)
+ assertThat(appList).isEmpty()
+ }
+
+ @Test
+ fun loadApps_matchAnyUserForAdminAndInstalledOnSecondaryUserOnly_displayed() = runTest {
+ val secondaryUserOnlyApp = ApplicationInfo().apply {
+ packageName = "installed.on.secondary.user.only"
+ }
+ mockInstalledApplications(listOf(secondaryUserOnlyApp), ADMIN_USER_ID)
+ mockInstalledApplications(emptyList(), MANAGED_PROFILE_USER_ID)
+
+ val appList = repository.loadApps(
+ userId = ADMIN_USER_ID,
+ matchAnyUserForAdmin = true,
+ )
+
+ assertThat(appList).containsExactly(secondaryUserOnlyApp)
}
@Test
@@ -106,11 +196,11 @@ class AppListRepositoryTest {
}
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(arrayOf(app.packageName))
- mockInstalledApplications(listOf(app))
+ mockInstalledApplications(listOf(app), ADMIN_USER_ID)
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
+ val appList = repository.loadApps(userId = ADMIN_USER_ID)
- assertThat(appListFlow).isEmpty()
+ assertThat(appList).isEmpty()
}
@Test
@@ -122,11 +212,11 @@ class AppListRepositoryTest {
}
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(arrayOf(app.packageName))
- mockInstalledApplications(listOf(app))
+ mockInstalledApplications(listOf(app), ADMIN_USER_ID)
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
+ val appList = repository.loadApps(userId = ADMIN_USER_ID)
- assertThat(appListFlow).isEmpty()
+ assertThat(appList).isEmpty()
}
@Test
@@ -137,11 +227,11 @@ class AppListRepositoryTest {
}
whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
.thenReturn(arrayOf(app.packageName))
- mockInstalledApplications(listOf(app))
+ mockInstalledApplications(listOf(app), ADMIN_USER_ID)
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
+ val appList = repository.loadApps(userId = ADMIN_USER_ID)
- assertThat(appListFlow).containsExactly(app)
+ assertThat(appList).containsExactly(app)
}
@Test
@@ -151,11 +241,11 @@ class AppListRepositoryTest {
enabled = false
enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
}
- mockInstalledApplications(listOf(app))
+ mockInstalledApplications(listOf(app), ADMIN_USER_ID)
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
+ val appList = repository.loadApps(userId = ADMIN_USER_ID)
- assertThat(appListFlow).containsExactly(app)
+ assertThat(appList).containsExactly(app)
}
@Test
@@ -164,11 +254,11 @@ class AppListRepositoryTest {
packageName = "disabled"
enabled = false
}
- mockInstalledApplications(listOf(app))
+ mockInstalledApplications(listOf(app), ADMIN_USER_ID)
- val appListFlow = repository.loadApps(userId = USER_ID, showInstantApps = false)
+ val appList = repository.loadApps(userId = ADMIN_USER_ID)
- assertThat(appListFlow).isEmpty()
+ assertThat(appList).isEmpty()
}
@Test
@@ -219,7 +309,11 @@ class AppListRepositoryTest {
val app = IN_LAUNCHER_APP
whenever(
- packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
+ packageManager.queryIntentActivitiesAsUser(
+ any(),
+ any<ResolveInfoFlags>(),
+ eq(ADMIN_USER_ID)
+ )
).thenReturn(listOf(resolveInfoOf(packageName = app.packageName)))
val showSystemPredicate = getShowSystemPredicate(showSystem = false)
@@ -229,12 +323,16 @@ class AppListRepositoryTest {
@Test
fun getSystemPackageNames_returnExpectedValues() = runTest {
- mockInstalledApplications(listOf(
- NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP))
+ mockInstalledApplications(
+ apps = listOf(
+ NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP
+ ),
+ userId = ADMIN_USER_ID,
+ )
val systemPackageNames = AppListRepositoryUtil.getSystemPackageNames(
context = context,
- userId = USER_ID,
+ userId = ADMIN_USER_ID,
showInstantApps = false,
)
@@ -243,12 +341,13 @@ class AppListRepositoryTest {
private suspend fun getShowSystemPredicate(showSystem: Boolean) =
repository.showSystemPredicate(
- userIdFlow = flowOf(USER_ID),
+ userIdFlow = flowOf(ADMIN_USER_ID),
showSystemFlow = flowOf(showSystem),
).first()
private companion object {
- const val USER_ID = 0
+ const val ADMIN_USER_ID = 0
+ const val MANAGED_PROFILE_USER_ID = 11
val NORMAL_APP = ApplicationInfo().apply {
packageName = "normal"
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
index 4f0cddef078b..36d9db5ef251 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt
@@ -85,7 +85,11 @@ class AppListViewModelTest {
}
private object FakeAppListRepository : AppListRepository {
- override suspend fun loadApps(userId: Int, showInstantApps: Boolean) = listOf(APP)
+ override suspend fun loadApps(
+ userId: Int,
+ showInstantApps: Boolean,
+ matchAnyUserForAdmin: Boolean,
+ ) = listOf(APP)
override fun showSystemPredicate(
userIdFlow: Flow<Int>,
@@ -112,7 +116,11 @@ class AppListViewModelTest {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
const val LABEL = "Label"
- val CONFIG = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false)
+ val CONFIG = AppListConfig(
+ userIds = listOf(USER_ID),
+ showInstantApps = false,
+ matchAnyUserForAdmin = false,
+ )
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
index a99d02de0df0..241a134b6a76 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTest.kt
@@ -114,7 +114,11 @@ class AppListTest {
) {
composeTestRule.setContent {
AppListInput(
- config = AppListConfig(userIds = listOf(USER_ID), showInstantApps = false),
+ config = AppListConfig(
+ userIds = listOf(USER_ID),
+ showInstantApps = false,
+ matchAnyUserForAdmin = false,
+ ),
listModel = TestAppListModel(enableGrouping = enableGrouping),
state = AppListState(
showSystem = false.toState(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index e22f3f0e720d..5fbb4c3e5712 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -37,8 +37,6 @@ import com.android.settingslib.Utils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -287,16 +285,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
return defaultValue;
}
- try {
- Method method = mService.getClass().getDeclaredMethod("getDeviceSideInternal",
- BluetoothDevice.class);
- method.setAccessible(true);
- return (int) method.invoke(mService, device);
- } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- Log.e(TAG, "fail to get getDeviceSideInternal\n" + e.toString() + "\n"
- + Log.getStackTraceString(new Throwable()));
- return defaultValue;
- }
+ return mService.getDeviceSide(device);
}
/**
@@ -313,17 +302,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
return defaultValue;
}
- try {
- Method method = mService.getClass().getDeclaredMethod("getDeviceModeInternal",
- BluetoothDevice.class);
- method.setAccessible(true);
- return (int) method.invoke(mService, device);
- } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- Log.e(TAG, "fail to get getDeviceModeInternal\n" + e.toString() + "\n"
- + Log.getStackTraceString(new Throwable()));
-
- return defaultValue;
- }
+ return mService.getDeviceMode(device);
}
public String toString() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 55125c53b4c9..049c90e971a9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -18,6 +18,9 @@ package com.android.settingslib.widget;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
@@ -87,4 +90,52 @@ public class FooterPreferenceTest {
assertThat(mFooterPreference.mIconVisibility).isEqualTo(View.GONE);
}
+
+ @Test
+ public void onBindViewHolder_whenTitleIsNull_shouldNotRaiseNpe() {
+ PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
+ when(viewHolder.findViewById(R.id.title)).thenReturn(null);
+
+ Throwable actualThrowable = null;
+ try {
+ mFooterPreference.onBindViewHolder(viewHolder);
+ } catch (Throwable throwable) {
+ actualThrowable = throwable;
+ }
+
+ assertThat(actualThrowable).isNull();
+ }
+
+ @Test
+ public void onBindViewHolder_whenLearnMoreIsNull_shouldNotRaiseNpe() {
+ PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
+ when(viewHolder.findViewById(R.id.settingslib_learn_more)).thenReturn(null);
+
+ Throwable actualThrowable = null;
+ try {
+ mFooterPreference.onBindViewHolder(viewHolder);
+ } catch (Throwable throwable) {
+ actualThrowable = throwable;
+ }
+
+ assertThat(actualThrowable).isNull();
+ }
+
+ @Test
+ public void onBindViewHolder_whenIconFrameIsNull_shouldNotRaiseNpe() {
+ PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
+ when(viewHolder.findViewById(R.id.icon_frame)).thenReturn(null);
+
+ Throwable actualThrowable = null;
+ try {
+ mFooterPreference.onBindViewHolder(viewHolder);
+ } catch (Throwable throwable) {
+ actualThrowable = throwable;
+ }
+
+ assertThat(actualThrowable).isNull();
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fedfb43535cc..78d93bd19a15 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -58,7 +58,6 @@
<uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
- <uses-permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index dabb5780621c..a00f401756f7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -984,7 +984,13 @@
android:exported="true"
android:excludeFromRecents="true"
android:resizeableActivity="false"
- android:theme="@android:style/Theme.NoDisplay" />
+ android:theme="@android:style/Theme.NoDisplay" >
+
+ <intent-filter>
+ <action android:name="com.android.systemui.action.LAUNCH_NOTE_TASK"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<!-- LaunchNoteTaskManagedProfileProxyActivity MUST NOT be exported because it allows caller
to specify an Android user when launching the default notes app. -->
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 322fc774e805..05630e795476 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -199,6 +199,9 @@ data class ClockConfig(
*/
val hasCustomPositionUpdatedAnimation: Boolean = false,
+ /** Transition to AOD should move smartspace like large clock instead of small clock */
+ val useAlternateSmartspaceAODTransition: Boolean = false,
+
/** True if the clock will react to tone changes in the seed color. */
val isReactiveToTone: Boolean = true,
)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
index f71c137363c5..2dd146c5134d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
@@ -47,6 +47,7 @@ constructor(
}
}
+ // Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
enum class WeatherStateIcon(val id: Int) {
UNKNOWN_ICON(0),
diff --git a/packages/SystemUI/res/drawable/chipbar_background.xml b/packages/SystemUI/res/drawable/chipbar_background.xml
index 57221776a32f..7530f5ba244a 100644
--- a/packages/SystemUI/res/drawable/chipbar_background.xml
+++ b/packages/SystemUI/res/drawable/chipbar_background.xml
@@ -17,6 +17,6 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <solid android:color="?androidprv:attr/colorAccentSecondary" />
+ <solid android:color="?androidprv:attr/materialColorSecondaryFixed" />
<corners android:radius="32dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/chipbar_end_button_background.xml b/packages/SystemUI/res/drawable/chipbar_end_button_background.xml
index 80c7207a35b6..a3832eef957f 100644
--- a/packages/SystemUI/res/drawable/chipbar_end_button_background.xml
+++ b/packages/SystemUI/res/drawable/chipbar_end_button_background.xml
@@ -20,7 +20,7 @@
android:color="?android:textColorPrimary">
<item android:id="@android:id/background">
<shape>
- <solid android:color="@android:color/system_accent1_200"/>
+ <solid android:color="?androidprv:attr/materialColorPrimaryFixedDim"/>
<corners android:radius="24dp" />
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/hearing.xml b/packages/SystemUI/res/drawable/hearing.xml
new file mode 100644
index 000000000000..02f5f92ec5fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M34.4,43.95Q31.55,43.95 29.45,42.4Q27.35,40.85 26.35,38.3Q25.35,35.75 24.375,34.325Q23.4,32.9 20.7,30.75Q17.4,28.1 15.95,25.1Q14.5,22.1 14.5,17.8Q14.5,11.8 18.3,7.975Q22.1,4.15 28.1,4.15Q34,4.15 37.875,7.825Q41.75,11.5 42,17.2H39Q38.75,12.8 35.725,9.975Q32.7,7.15 28.1,7.15Q23.6,7.15 20.55,10.225Q17.5,13.3 17.5,17.8Q17.5,21.4 18.9,24.025Q20.3,26.65 23.55,29.1Q25.5,30.55 26.675,32.25Q27.85,33.95 28.9,36.45Q29.75,38.55 31.125,39.75Q32.5,40.95 34.4,40.95Q36.15,40.95 37.425,39.75Q38.7,38.55 38.95,36.8H41.95Q41.7,39.8 39.55,41.875Q37.4,43.95 34.4,43.95ZM11.95,32.9Q9.1,29.75 7.55,25.825Q6,21.9 6,17.6Q6,13.35 7.475,9.375Q8.95,5.4 11.95,2.35L14.2,4.35Q11.6,7 10.3,10.425Q9,13.85 9,17.6Q9,21.3 10.325,24.725Q11.65,28.15 14.2,30.85ZM28.1,22.45Q26.15,22.45 24.8,21.1Q23.45,19.75 23.45,17.8Q23.45,15.85 24.8,14.45Q26.15,13.05 28.1,13.05Q30.05,13.05 31.45,14.45Q32.85,15.85 32.85,17.8Q32.85,19.75 31.45,21.1Q30.05,22.45 28.1,22.45Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index 762dcdced9c4..8fa975be43d2 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -49,15 +49,17 @@
android:alpha="0.0"
/>
+ <!-- LINT.IfChange textColor -->
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textSize="@dimen/chipbar_text_size"
- android:textColor="@color/chipbar_text_and_icon_color"
+ style="@style/Chipbar.Text"
+ android:textColor="?androidprv:attr/materialColorOnSecondaryFixed"
android:alpha="0.0"
/>
+ <!-- LINT.ThenChange(systemui.temporarydisplay.chipbar.ChipbarInfo.kt) -->
<!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
<ImageView
@@ -66,7 +68,7 @@
android:layout_height="@dimen/chipbar_end_icon_size"
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
android:src="@drawable/ic_progress_activity"
- android:tint="@android:color/system_accent2_700"
+ android:tint="?androidprv:attr/materialColorOnSecondaryFixedVariant"
android:alpha="0.0"
/>
@@ -84,9 +86,9 @@
android:id="@+id/end_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?androidprv:attr/textColorOnAccent"
android:layout_marginStart="@dimen/chipbar_end_item_start_margin"
- android:textSize="@dimen/chipbar_text_size"
+ style="@style/Chipbar.Text"
+ android:textColor="?androidprv:attr/materialColorOnPrimaryFixed"
android:paddingStart="@dimen/chipbar_outer_padding"
android:paddingEnd="@dimen/chipbar_outer_padding"
android:paddingTop="@dimen/chipbar_end_button_vertical_padding"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 96e6d4e1a234..cb8c2a73c15d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -231,9 +231,6 @@
<color name="people_tile_background">@color/material_dynamic_secondary95</color>
- <!-- Chipbar -->
- <color name="chipbar_text_and_icon_color">@android:color/system_accent2_900</color>
-
<!-- Internet Dialog -->
<!-- Material next state on color-->
<color name="settingslib_state_on_color">@color/settingslib_state_on</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4db42aacbdcd..0eb0a078a478 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1110,6 +1110,7 @@
<!-- Chipbar -->
<!-- (Used for media tap-to-transfer chip for sender device and active unlock) -->
<dimen name="chipbar_outer_padding">16dp</dimen>
+ <dimen name="chipbar_outer_padding_half">8dp</dimen>
<dimen name="chipbar_text_size">16sp</dimen>
<dimen name="chipbar_start_icon_size">24dp</dimen>
<dimen name="chipbar_end_icon_size">20dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2098aea87ab2..a359d3b21938 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -70,6 +70,15 @@
<item name="android:fontWeight">700</item>
</style>
+ <style name="Chipbar" />
+
+ <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
+ <!-- Text size should be kept in sync with the notification conversation header size. (The
+ conversation header doesn't have a defined style, so the size must be copied here.)
+ See notification_template_conversation_header.xml. -->
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="TextAppearance" />
<style name="TextAppearance.QS">
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 0779653430b2..7262a736c433 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -25,6 +25,7 @@ import android.graphics.Rect
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
+import android.view.View.OnAttachStateChangeListener
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
@@ -105,6 +106,24 @@ constructor(
}
updateFontSizes()
updateTimeListeners()
+ value.smallClock.view.addOnAttachStateChangeListener(
+ object : OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(p0: View?) {
+ value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+
+ override fun onViewDetachedFromWindow(p0: View?) {
+ }
+ })
+ value.largeClock.view.addOnAttachStateChangeListener(
+ object : OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(p0: View?) {
+ value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+
+ override fun onViewDetachedFromWindow(p0: View?) {
+ }
+ })
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 87a775866faf..db38d3414915 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -75,6 +75,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -115,6 +116,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final SessionTracker mSessionTracker;
private final Optional<SideFpsController> mSideFpsController;
private final FalsingA11yDelegate mFalsingA11yDelegate;
+ private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
private int mTranslationY;
// Whether the volume keys should be handled by keyguard. If true, then
// they will be handled here for specific media types such as music, otherwise
@@ -300,6 +302,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
@Override
public void onSwipeUp() {
if (!mUpdateMonitor.isFaceDetectionRunning()) {
+ mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER);
mKeyguardSecurityCallback.userActivity();
@@ -389,7 +392,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
FalsingA11yDelegate falsingA11yDelegate,
TelephonyManager telephonyManager,
ViewMediatorCallback viewMediatorCallback,
- AudioManager audioManager
+ AudioManager audioManager,
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor
) {
super(view);
mLockPatternUtils = lockPatternUtils;
@@ -414,6 +418,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mTelephonyManager = telephonyManager;
mViewMediatorCallback = viewMediatorCallback;
mAudioManager = audioManager;
+ mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index b8e196fb8787..d8e1eb0f0860 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -19,10 +19,12 @@ package com.android.keyguard;
import static java.util.Collections.emptySet;
import android.content.Context;
+import android.os.Build;
import android.os.Trace;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.widget.GridLayout;
import com.android.systemui.R;
@@ -118,6 +120,16 @@ public class KeyguardStatusView extends GridLayout {
}
@Override
+ public ViewPropertyAnimator animate() {
+ if (Build.IS_DEBUGGABLE) {
+ throw new IllegalArgumentException(
+ "KeyguardStatusView does not support ViewPropertyAnimator. "
+ + "Use PropertyAnimator instead.");
+ }
+ return super.animate();
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Trace.beginSection("KeyguardStatusView#onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 6f549881d12a..0cdef4d63639 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -73,7 +73,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
*/
private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000;
- private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES =
+ public static final AnimationProperties CLOCK_ANIMATION_PROPERTIES =
new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private final KeyguardSliceViewController mKeyguardSliceViewController;
@@ -221,16 +221,32 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mView.setImportantForAccessibility(mode);
}
+ @VisibleForTesting
+ void setProperty(AnimatableProperty property, float value, boolean animate) {
+ PropertyAnimator.setProperty(mView, property, value, CLOCK_ANIMATION_PROPERTIES, animate);
+ }
+
/**
* Update position of the view with an optional animation
*/
public void updatePosition(int x, int y, float scale, boolean animate) {
float oldY = mView.getY();
- PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
- animate);
+ setProperty(AnimatableProperty.Y, y, animate);
+
+ ClockController clock = mKeyguardClockSwitchController.getClock();
+ if (clock != null && clock.getConfig().getUseAlternateSmartspaceAODTransition()) {
+ // If requested, scale the entire view instead of just the clock view
+ mKeyguardClockSwitchController.updatePosition(x, 1f /* scale */,
+ CLOCK_ANIMATION_PROPERTIES, animate);
+ setProperty(AnimatableProperty.SCALE_X, scale, animate);
+ setProperty(AnimatableProperty.SCALE_Y, scale, animate);
+ } else {
+ mKeyguardClockSwitchController.updatePosition(x, scale,
+ CLOCK_ANIMATION_PROPERTIES, animate);
+ setProperty(AnimatableProperty.SCALE_X, 1f, animate);
+ setProperty(AnimatableProperty.SCALE_Y, 1f, animate);
+ }
- mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
- animate);
if (oldY != y) {
mKeyguardClockSwitchController.updateKeyguardStatusViewOffset();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 350c4ed084b9..33a822440866 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -154,6 +154,15 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
+import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.DetectionStatus;
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.WeatherData;
@@ -372,6 +381,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final FingerprintManager mFpm;
@Nullable
private final FaceManager mFaceManager;
+ @Nullable
+ private KeyguardFaceAuthInteractor mFaceAuthInteractor;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@DevicePostureInt
@@ -1165,8 +1176,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
Trace.endSection();
}
+ /**
+ * @deprecated This is being migrated to use modern architecture, this method is visible purely
+ * for bridging the gap while the migration is active.
+ */
private void handleFaceAuthFailed() {
Assert.isMainThread();
+ String reason =
+ mKeyguardBypassController.canBypass() ? "bypass"
+ : mAlternateBouncerShowing ? "alternateBouncer"
+ : mPrimaryBouncerFullyShown ? "bouncer"
+ : "udfpsFpDown";
+ requestActiveUnlock(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
+ "faceFailure-" + reason);
+
mLogger.d("onFaceAuthFailed");
mFaceCancelSignal = null;
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
@@ -1180,6 +1204,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mContext.getString(R.string.kg_face_not_recognized));
}
+ /**
+ * @deprecated This is being migrated to use modern architecture, this method is visible purely
+ * for bridging the gap while the migration is active.
+ */
private void handleFaceAcquired(int acquireInfo) {
Assert.isMainThread();
mLogger.logFaceAcquired(acquireInfo);
@@ -1189,8 +1217,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onBiometricAcquired(FACE, acquireInfo);
}
}
+
+ if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ acquireInfo)) {
+ requestActiveUnlock(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
+ "faceAcquireInfo-" + acquireInfo);
+ }
}
+ /**
+ * @deprecated This is being migrated to use modern architecture, this method is visible purely
+ * for bridging the gap while the migration is active.
+ */
private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
try {
@@ -1203,7 +1242,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.logFaceAuthForWrongUser(authUserId);
return;
}
- if (isFaceDisabled(userId)) {
+ if (!isFaceAuthInteractorEnabled() && isFaceDisabled(userId)) {
mLogger.logFaceAuthDisabledForUser(userId);
return;
}
@@ -1215,6 +1254,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
Trace.endSection();
}
+ /**
+ * @deprecated This is being migrated to use modern architecture, this method is visible purely
+ * for bridging the gap while the migration is active.
+ */
private void handleFaceHelp(int msgId, String helpString) {
Assert.isMainThread();
mLogger.logFaceAuthHelpMsg(msgId, helpString);
@@ -1226,22 +1269,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- private final Runnable mRetryFaceAuthentication = new Runnable() {
- @Override
- public void run() {
- mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount);
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
- FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE);
- }
- };
-
- private void onFaceCancelNotReceived() {
- mLogger.e("Face cancellation not received, transitioning to STOPPED");
- mFaceRunningState = BIOMETRIC_STATE_STOPPED;
- KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP,
- FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED);
- }
-
+ /**
+ * @deprecated This is being migrated to use modern architecture, this method is visible purely
+ * for bridging the gap while the migration is active.
+ */
private void handleFaceError(int msgId, final String originalErrMsg) {
Assert.isMainThread();
String errString = originalErrMsg;
@@ -1299,6 +1330,28 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (lockedOutStateChanged) {
notifyLockedOutStateChanged(FACE);
}
+
+ if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(msgId)) {
+ requestActiveUnlock(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
+ "faceError-" + msgId);
+ }
+ }
+
+ private final Runnable mRetryFaceAuthentication = new Runnable() {
+ @Override
+ public void run() {
+ mLogger.logRetryingAfterFaceHwUnavailable(mHardwareFaceUnavailableRetryCount);
+ updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
+ FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE);
+ }
+ };
+
+ private void onFaceCancelNotReceived() {
+ mLogger.e("Face cancellation not received, transitioning to STOPPED");
+ mFaceRunningState = BIOMETRIC_STATE_STOPPED;
+ KeyguardUpdateMonitor.this.updateFaceListeningState(BIOMETRIC_ACTION_STOP,
+ FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED);
}
private void handleFaceLockoutReset(@LockoutMode int mode) {
@@ -1343,10 +1396,61 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
}
+ /**
+ * @deprecated This is being migrated to use modern architecture.
+ */
+ @Deprecated
public boolean isFaceDetectionRunning() {
+ if (isFaceAuthInteractorEnabled()) {
+ return getFaceAuthInteractor().isRunning();
+ }
return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
}
+ private boolean isFaceAuthInteractorEnabled() {
+ return mFaceAuthInteractor != null && mFaceAuthInteractor.isEnabled();
+ }
+
+ private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() {
+ return mFaceAuthInteractor;
+ }
+
+ /**
+ * Set the face auth interactor that should be used for initiating face authentication.
+ */
+ public void setFaceAuthInteractor(@Nullable KeyguardFaceAuthInteractor faceAuthInteractor) {
+ mFaceAuthInteractor = faceAuthInteractor;
+ mFaceAuthInteractor.registerListener(mFaceAuthenticationListener);
+ }
+
+ private FaceAuthenticationListener mFaceAuthenticationListener =
+ new FaceAuthenticationListener() {
+ @Override
+ public void onAuthenticationStatusChanged(@NonNull AuthenticationStatus status) {
+ if (status instanceof AcquiredAuthenticationStatus) {
+ handleFaceAcquired(
+ ((AcquiredAuthenticationStatus) status).getAcquiredInfo());
+ } else if (status instanceof ErrorAuthenticationStatus) {
+ ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status;
+ handleFaceError(error.getMsgId(), error.getMsg());
+ } else if (status instanceof FailedAuthenticationStatus) {
+ handleFaceAuthFailed();
+ } else if (status instanceof HelpAuthenticationStatus) {
+ HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status;
+ handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg());
+ } else if (status instanceof SuccessAuthenticationStatus) {
+ FaceManager.AuthenticationResult result =
+ ((SuccessAuthenticationStatus) status).getSuccessResult();
+ handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
+ }
+ }
+
+ @Override
+ public void onDetectionStatusChanged(@NonNull DetectionStatus status) {
+ handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric());
+ }
+ };
+
private boolean isTrustDisabled() {
// Don't allow trust agent if device is secured with a SIM PIN. This is here
// mainly because there's no other way to prompt the user to enter their SIM PIN
@@ -1360,6 +1464,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| isSimPinSecure();
}
+ /**
+ * @deprecated This method is not needed anymore with the new face auth system.
+ */
+ @Deprecated
private boolean isFaceDisabled(int userId) {
// TODO(b/140035044)
return whitelistIpcs(() ->
@@ -1371,7 +1479,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* @return whether the current user has been authenticated with face. This may be true
* on the lockscreen if the user doesn't have bypass enabled.
+ *
+ * @deprecated This is being migrated to use modern architecture.
*/
+ @Deprecated
public boolean getIsFaceAuthenticated() {
boolean faceAuthenticated = false;
BiometricAuthenticated bioFaceAuthenticated = mUserFaceAuthenticated.get(getCurrentUser());
@@ -1619,6 +1730,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
void setAssistantVisible(boolean assistantVisible) {
mAssistantVisible = assistantVisible;
mLogger.logAssistantVisible(mAssistantVisible);
+ if (isFaceAuthInteractorEnabled()) {
+ mFaceAuthInteractor.onAssistantTriggeredOnLockScreen();
+ }
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED);
if (mAssistantVisible) {
@@ -1832,54 +1946,27 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onAuthenticationFailed() {
- String reason =
- mKeyguardBypassController.canBypass() ? "bypass"
- : mAlternateBouncerShowing ? "alternateBouncer"
- : mPrimaryBouncerFullyShown ? "bouncer"
- : "udfpsFpDown";
- requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
- "faceFailure-" + reason);
-
handleFaceAuthFailed();
}
@Override
public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
- Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
- Trace.endSection();
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
- if (mFaceAcquiredInfoIgnoreList.contains(helpMsgId)) {
- return;
- }
handleFaceHelp(helpMsgId, helpString.toString());
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
handleFaceError(errMsgId, errString.toString());
-
- if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) {
- requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
- "faceError-" + errMsgId);
- }
}
@Override
public void onAuthenticationAcquired(int acquireInfo) {
handleFaceAcquired(acquireInfo);
-
- if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- acquireInfo)) {
- requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
- "faceAcquireInfo-" + acquireInfo);
- }
}
};
@@ -2628,7 +2715,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* @param reason One of the reasons {@link FaceAuthApiRequestReason} on why this API is being
* invoked.
* @return current face auth detection state, true if it is running.
+ * @deprecated This is being migrated to use modern architecture.
*/
+ @Deprecated
public boolean requestFaceAuth(@FaceAuthApiRequestReason String reason) {
mLogger.logFaceAuthRequested(reason);
updateFaceListeningState(BIOMETRIC_ACTION_START, apiRequestReasonToUiEvent(reason));
@@ -2643,6 +2732,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private void updateFaceListeningState(int action, @NonNull FaceAuthUiEvent faceAuthUiEvent) {
+ if (isFaceAuthInteractorEnabled()) return;
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
@@ -3154,6 +3244,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
public boolean isFaceLockedOut() {
+ if (isFaceAuthInteractorEnabled()) {
+ return getFaceAuthInteractor().isLockedOut();
+ }
return mFaceLockedOutPermanent;
}
@@ -3202,13 +3295,23 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
}
+ /**
+ * @deprecated This is being migrated to use modern architecture.
+ */
+ @Deprecated
private boolean isUnlockWithFacePossible(int userId) {
+ if (isFaceAuthInteractorEnabled()) {
+ return getFaceAuthInteractor().canFaceAuthRun();
+ }
return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
}
/**
* If face hardware is available, user has enrolled and enabled auth via setting.
+ *
+ * @deprecated This is being migrated to use modern architecture.
*/
+ @Deprecated
public boolean isFaceAuthEnabledForUser(int userId) {
// TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
updateFaceEnrolled(userId);
@@ -3232,6 +3335,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private void stopListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
+ if (isFaceAuthInteractorEnabled()) return;
mLogger.v("stopListeningForFace()");
mLogger.logStoppedListeningForFace(mFaceRunningState, faceAuthUiEvent.getReason());
mUiEventLogger.log(faceAuthUiEvent, getKeyguardSessionId());
@@ -4098,6 +4202,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mStatusBarStateController.removeCallback(mStatusBarStateControllerListener);
mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener);
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
+ if (isFaceAuthInteractorEnabled()) {
+ mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener);
+ }
if (mDeviceProvisionedObserver != null) {
mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);
@@ -4127,6 +4234,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
pw.println(" getUserUnlockedWithBiometric()="
+ getUserUnlockedWithBiometric(getCurrentUser()));
+ pw.println(" isFaceAuthInteractorEnabled: " + isFaceAuthInteractorEnabled());
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index a678edc0eb06..651c9796140e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -18,8 +18,8 @@ package com.android.keyguard;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import android.util.Property;
import android.view.View;
-import android.view.ViewPropertyAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.log.LogBuffer;
@@ -34,6 +34,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.errorprone.annotations.CompileTimeConstant;
+import java.util.function.Consumer;
+
/**
* Helper class for updating visibility of keyguard views based on keyguard and status bar state.
* This logic is shared by both the keyguard status view and the keyguard user switcher.
@@ -83,47 +85,49 @@ public class KeyguardVisibilityHelper {
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
- mView.animate().cancel();
+ PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA);
boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
&& statusBarState != KEYGUARD) || goingToFullShade) {
mKeyguardViewVisibilityAnimating = true;
- mView.animate()
- .alpha(0f)
- .setStartDelay(0)
- .setDuration(160)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(
- mAnimateKeyguardStatusViewGoneEndRunnable);
+
+ AnimationProperties animProps = new AnimationProperties()
+ .setCustomInterpolator(View.ALPHA, Interpolators.ALPHA_OUT)
+ .setAnimationEndAction(mSetGoneEndAction);
if (keyguardFadingAway) {
- mView.animate()
- .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
- .start();
+ animProps
+ .setDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+ .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration());
log("goingToFullShade && keyguardFadingAway");
} else {
+ animProps.setDelay(0).setDuration(160);
log("goingToFullShade && !keyguardFadingAway");
}
+ PropertyAnimator.setProperty(
+ mView, AnimatableProperty.ALPHA, 0f, animProps, true /* animate */);
} else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
mView.setVisibility(View.VISIBLE);
mKeyguardViewVisibilityAnimating = true;
mView.setAlpha(0f);
- mView.animate()
- .alpha(1f)
- .setStartDelay(0)
- .setDuration(320)
- .setInterpolator(Interpolators.ALPHA_IN)
- .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
+ PropertyAnimator.setProperty(
+ mView, AnimatableProperty.ALPHA, 1f,
+ new AnimationProperties()
+ .setDelay(0)
+ .setDuration(320)
+ .setCustomInterpolator(View.ALPHA, Interpolators.ALPHA_IN)
+ .setAnimationEndAction(
+ property -> mSetVisibleEndRunnable.run()),
+ true /* animate */);
log("keyguardFadingAway transition w/ Y Aniamtion");
} else if (statusBarState == KEYGUARD) {
if (keyguardFadingAway) {
mKeyguardViewVisibilityAnimating = true;
- ViewPropertyAnimator animator = mView.animate()
- .alpha(0)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable);
+ AnimationProperties animProps = new AnimationProperties()
+ .setDelay(0)
+ .setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_LINEAR_IN)
+ .setAnimationEndAction(mSetInvisibleEndAction);
if (mAnimateYPos) {
float target = mView.getY() - mView.getHeight() * 0.05f;
int delay = 0;
@@ -135,13 +139,16 @@ public class KeyguardVisibilityHelper {
PropertyAnimator.setProperty(mView, AnimatableProperty.Y, target,
mAnimationProperties,
true /* animate */);
- animator.setDuration(duration)
- .setStartDelay(delay);
+ animProps.setDuration(duration)
+ .setDelay(delay);
log("keyguardFadingAway transition w/ Y Aniamtion");
} else {
log("keyguardFadingAway transition w/o Y Animation");
}
- animator.start();
+ PropertyAnimator.setProperty(
+ mView, AnimatableProperty.ALPHA, 0f,
+ animProps,
+ true /* animate */);
} else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) {
log("ScreenOff transition");
mKeyguardViewVisibilityAnimating = true;
@@ -149,7 +156,7 @@ public class KeyguardVisibilityHelper {
// Ask the screen off animation controller to animate the keyguard visibility for us
// since it may need to be cancelled due to keyguard lifecycle events.
mScreenOffAnimationController.animateInKeyguard(
- mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
+ mView, mSetVisibleEndRunnable);
} else {
log("Direct set Visibility to VISIBLE");
mView.setVisibility(View.VISIBLE);
@@ -163,19 +170,25 @@ public class KeyguardVisibilityHelper {
mLastOccludedState = isOccluded;
}
- private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
- mKeyguardViewVisibilityAnimating = false;
- mView.setVisibility(View.INVISIBLE);
- log("Callback Set Visibility to INVISIBLE");
+ private final Consumer<Property> mSetInvisibleEndAction = new Consumer<>() {
+ @Override
+ public void accept(Property property) {
+ mKeyguardViewVisibilityAnimating = false;
+ mView.setVisibility(View.INVISIBLE);
+ log("Callback Set Visibility to INVISIBLE");
+ }
};
- private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
- mKeyguardViewVisibilityAnimating = false;
- mView.setVisibility(View.GONE);
- log("CallbackSet Visibility to GONE");
+ private final Consumer<Property> mSetGoneEndAction = new Consumer<>() {
+ @Override
+ public void accept(Property property) {
+ mKeyguardViewVisibilityAnimating = false;
+ mView.setVisibility(View.GONE);
+ log("CallbackSet Visibility to GONE");
+ }
};
- private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
+ private final Runnable mSetVisibleEndRunnable = () -> {
mKeyguardViewVisibilityAnimating = false;
mView.setVisibility(View.VISIBLE);
log("Callback Set Visibility to VISIBLE");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index aabdafb88558..7a237591a212 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -83,6 +83,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
@@ -151,6 +152,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final DumpManager mDumpManager;
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@NonNull private final VibratorHelper mVibrator;
@NonNull private final FeatureFlags mFeatureFlags;
@NonNull private final FalsingManager mFalsingManager;
@@ -818,7 +820,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull AlternateBouncerInteractor alternateBouncerInteractor,
@NonNull SecureSettings secureSettings,
@NonNull InputManager inputManager,
- @NonNull UdfpsUtils udfpsUtils) {
+ @NonNull UdfpsUtils udfpsUtils,
+ @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -882,6 +885,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
return Unit.INSTANCE;
});
+ mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
@@ -1141,6 +1145,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!mOnFingerDown) {
playStartHaptic();
+ mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
if (!mKeyguardUpdateMonitor.isFaceDetectionRunning()) {
mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
index 6a6c3eb05399..0869351e727b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/TintedIcon.kt
@@ -16,10 +16,10 @@
package com.android.systemui.common.shared.model
-import androidx.annotation.ColorRes
+import androidx.annotation.AttrRes
/** Models an icon with a specific tint. */
data class TintedIcon(
val icon: Icon,
- @ColorRes val tint: Int?,
+ @AttrRes val tint: Int?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
index bcc5932dcf30..5c5723fa0a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/TintedIconViewBinder.kt
@@ -17,6 +17,7 @@
package com.android.systemui.common.ui.binder
import android.widget.ImageView
+import com.android.settingslib.Utils
import com.android.systemui.common.shared.model.TintedIcon
object TintedIconViewBinder {
@@ -33,7 +34,7 @@ object TintedIconViewBinder {
IconViewBinder.bind(tintedIcon.icon, view)
view.imageTintList =
if (tintedIcon.tint != null) {
- view.resources.getColorStateList(tintedIcon.tint, view.context.theme)
+ Utils.getColorAttr(view.context, tintedIcon.tint)
} else {
null
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 7f44463f1191..aca621bd9e55 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -41,6 +41,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -195,7 +196,14 @@ public class DreamOverlayTouchMonitor {
* Called by the monitor when this session is removed.
*/
private void onRemoved() {
- mCallbacks.forEach(callback -> callback.onRemoved());
+ mEventListeners.clear();
+ mGestureListeners.clear();
+ final Iterator<Callback> iter = mCallbacks.iterator();
+ while (iter.hasNext()) {
+ final Callback callback = iter.next();
+ callback.onRemoved();
+ iter.remove();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 012c8cfdd115..f28aeadc7acb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -108,11 +108,6 @@ object Flags {
val NOTIFICATION_SHELF_REFACTOR =
unreleasedFlag(271161129, "notification_shelf_refactor")
- // TODO(b/263414400): Tracking Bug
- @JvmField
- val NOTIFICATION_ANIMATE_BIG_PICTURE =
- releasedFlag(120, "notification_animate_big_picture")
-
@JvmField
val ANIMATED_NOTIFICATION_SHADE_INSETS =
releasedFlag(270682168, "animated_notification_shade_insets")
@@ -605,9 +600,6 @@ object Flags {
// TODO(b/254512507): Tracking Bug
val CHOOSER_UNBUNDLED = releasedFlag(1500, "chooser_unbundled")
- // TODO(b/266983432) Tracking Bug
- val SHARESHEET_CUSTOM_ACTIONS = releasedFlag(1501, "sharesheet_custom_actions")
-
// TODO(b/266982749) Tracking Bug
val SHARESHEET_RESELECTION_ACTION = releasedFlag(1502, "sharesheet_reselection_action")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
new file mode 100644
index 000000000000..a44df7e11fa1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.dagger
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor
+import dagger.Binds
+import dagger.Module
+
+/**
+ * Module that provides bindings for face auth classes that are injected into SysUI components that
+ * are used across different SysUI variants, where face auth is not supported.
+ *
+ * Some variants that do not support face authentication can install this module to provide a no-op
+ * implementation of the interactor.
+ */
+@Module
+interface KeyguardFaceAuthNotSupportedModule {
+ @Binds
+ fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 0abce82527f9..c4fc8834df83 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -44,6 +44,8 @@ import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
@@ -78,7 +80,7 @@ interface DeviceEntryFaceAuthRepository {
val isAuthenticated: Flow<Boolean>
/** Whether face auth can run at this point. */
- val canRunFaceAuth: Flow<Boolean>
+ val canRunFaceAuth: StateFlow<Boolean>
/** Provide the current status of face authentication. */
val authenticationStatus: Flow<AuthenticationStatus>
@@ -87,10 +89,10 @@ interface DeviceEntryFaceAuthRepository {
val detectionStatus: Flow<DetectionStatus>
/** Current state of whether face authentication is locked out or not. */
- val isLockedOut: Flow<Boolean>
+ val isLockedOut: StateFlow<Boolean>
/** Current state of whether face authentication is running. */
- val isAuthRunning: Flow<Boolean>
+ val isAuthRunning: StateFlow<Boolean>
/** Whether bypass is currently enabled */
val isBypassEnabled: Flow<Boolean>
@@ -129,6 +131,8 @@ constructor(
private val keyguardRepository: KeyguardRepository,
private val keyguardInteractor: KeyguardInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ @FaceDetectTableLog private val faceDetectLog: TableLogBuffer,
+ @FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
dumpManager: DumpManager,
) : DeviceEntryFaceAuthRepository, Dumpable {
private var authCancellationSignal: CancellationSignal? = null
@@ -224,17 +228,19 @@ constructor(
// Face detection can run only when lockscreen bypass is enabled
// & detection is supported & biometric unlock is not allowed.
listOf(
- canFaceAuthOrDetectRun(),
- logAndObserve(isBypassEnabled, "isBypassEnabled"),
+ canFaceAuthOrDetectRun(faceDetectLog),
+ logAndObserve(isBypassEnabled, "isBypassEnabled", faceDetectLog),
logAndObserve(
biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(),
- "nonStrongBiometricIsNotAllowed"
+ "nonStrongBiometricIsNotAllowed",
+ faceDetectLog
),
// We don't want to run face detect if it's not possible to authenticate with FP
// from the bouncer. UDFPS is the only fp sensor type that won't support this.
logAndObserve(
and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
- "udfpsAuthIsNotPossibleAnymore"
+ "udfpsAuthIsNotPossibleAnymore",
+ faceDetectLog
)
)
.reduce(::and)
@@ -246,6 +252,7 @@ constructor(
cancelDetection()
}
}
+ .logDiffsForTable(faceDetectLog, "", "canFaceDetectRun", false)
.launchIn(applicationScope)
}
@@ -254,26 +261,34 @@ constructor(
it == BiometricType.UNDER_DISPLAY_FINGERPRINT
}
- private fun canFaceAuthOrDetectRun(): Flow<Boolean> {
+ private fun canFaceAuthOrDetectRun(tableLogBuffer: TableLogBuffer): Flow<Boolean> {
return listOf(
- logAndObserve(biometricSettingsRepository.isFaceEnrolled, "isFaceEnrolled"),
+ logAndObserve(
+ biometricSettingsRepository.isFaceEnrolled,
+ "isFaceEnrolled",
+ tableLogBuffer
+ ),
logAndObserve(
biometricSettingsRepository.isFaceAuthenticationEnabled,
- "isFaceAuthenticationEnabled"
+ "isFaceAuthenticationEnabled",
+ tableLogBuffer
),
logAndObserve(
userRepository.userSwitchingInProgress.isFalse(),
- "userSwitchingNotInProgress"
+ "userSwitchingNotInProgress",
+ tableLogBuffer
),
logAndObserve(
keyguardRepository.isKeyguardGoingAway.isFalse(),
- "keyguardNotGoingAway"
+ "keyguardNotGoingAway",
+ tableLogBuffer
),
logAndObserve(
keyguardRepository.wakefulness
.map { WakefulnessModel.isSleepingOrStartingToSleep(it) }
.isFalse(),
- "deviceNotSleepingOrNotStartingToSleep"
+ "deviceNotSleepingOrNotStartingToSleep",
+ tableLogBuffer
),
logAndObserve(
combine(
@@ -282,15 +297,18 @@ constructor(
) { a, b ->
!a || b
},
- "secureCameraNotActiveOrAltBouncerIsShowing"
+ "secureCameraNotActiveOrAltBouncerIsShowing",
+ tableLogBuffer
),
logAndObserve(
biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture,
- "isFaceAuthSupportedInCurrentPosture"
+ "isFaceAuthSupportedInCurrentPosture",
+ tableLogBuffer
),
logAndObserve(
biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
- "userHasNotLockedDownDevice"
+ "userHasNotLockedDownDevice",
+ tableLogBuffer
)
)
.reduce(::and)
@@ -299,20 +317,27 @@ constructor(
private fun observeFaceAuthGatingChecks() {
// Face auth can run only if all of the gating conditions are true.
listOf(
- canFaceAuthOrDetectRun(),
- logAndObserve(isLockedOut.isFalse(), "isNotLocked"),
+ canFaceAuthOrDetectRun(faceAuthLog),
+ logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog),
logAndObserve(
deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
- "fpLockedOut"
+ "fpLockedOut",
+ faceAuthLog
+ ),
+ logAndObserve(
+ trustRepository.isCurrentUserTrusted.isFalse(),
+ "currentUserTrusted",
+ faceAuthLog
),
- logAndObserve(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserTrusted"),
logAndObserve(
biometricSettingsRepository.isNonStrongBiometricAllowed,
- "nonStrongBiometricIsAllowed"
+ "nonStrongBiometricIsAllowed",
+ faceAuthLog
),
logAndObserve(
userRepository.selectedUserInfo.map { it.isPrimary },
- "userIsPrimaryUser"
+ "userIsPrimaryUser",
+ faceAuthLog
),
)
.reduce(::and)
@@ -326,6 +351,7 @@ constructor(
cancel()
}
}
+ .logDiffsForTable(faceAuthLog, "", "canFaceAuthRun", false)
.launchIn(applicationScope)
}
@@ -340,7 +366,6 @@ constructor(
override fun onAuthenticationAcquired(acquireInfo: Int) {
_authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
- faceAuthLogger.authenticationAcquired(acquireInfo)
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
@@ -401,7 +426,7 @@ constructor(
override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
if (_isAuthRunning.value) {
- faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
+ faceAuthLogger.ignoredFaceAuthTrigger(uiEvent, "face auth is currently running")
return
}
@@ -438,7 +463,16 @@ constructor(
)
}
} else if (fallbackToDetection && canRunDetection.value) {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent,
+ "face auth gating check is false, falling back to detection."
+ )
detect()
+ } else {
+ faceAuthLogger.ignoredFaceAuthTrigger(
+ uiEvent,
+ "face auth & detect gating check is false"
+ )
}
}
@@ -467,7 +501,7 @@ constructor(
private val currentUserId: Int
get() = userRepository.getSelectedUserInfo().id
- fun cancelDetection() {
+ private fun cancelDetection() {
detectCancellationSignal?.cancel()
detectCancellationSignal = null
}
@@ -491,10 +525,20 @@ constructor(
_isAuthRunning.value = false
}
- private fun logAndObserve(cond: Flow<Boolean>, loggingContext: String): Flow<Boolean> {
- return cond.distinctUntilChanged().onEach {
- faceAuthLogger.observedConditionChanged(it, loggingContext)
- }
+ private fun logAndObserve(
+ cond: Flow<Boolean>,
+ conditionName: String,
+ logBuffer: TableLogBuffer
+ ): Flow<Boolean> {
+ return cond
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ logBuffer,
+ columnName = conditionName,
+ columnPrefix = "",
+ initialValue = false
+ )
+ .onEach { faceAuthLogger.observedConditionChanged(it, conditionName) }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt
new file mode 100644
index 000000000000..6c23032143c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceAuthTableLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import javax.inject.Qualifier
+
+/** Face auth logs in table format. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class FaceAuthTableLog
diff --git a/core/java/com/android/internal/expresslog/Utils.java b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceDetectTableLog.kt
index d82192f51662..342064f02ef2 100644
--- a/core/java/com/android/internal/expresslog/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/FaceDetectTableLog.kt
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package com.android.internal.expresslog;
+package com.android.systemui.keyguard.data.repository
-final class Utils {
- static native long hashString(String stringToHash);
-}
+import javax.inject.Qualifier
+
+/** Face detect logs in table format. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class FaceDetectTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
index 3c66f2424c7b..ef8b40191149 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -17,8 +17,17 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
import dagger.Binds
import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
@Module
interface KeyguardFaceAuthModule {
@@ -27,5 +36,31 @@ interface KeyguardFaceAuthModule {
impl: DeviceEntryFaceAuthRepositoryImpl
): DeviceEntryFaceAuthRepository
+ @Binds
+ @IntoMap
+ @ClassKey(SystemUIKeyguardFaceAuthInteractor::class)
+ fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable
+
+ @Binds
+ fun keyguardFaceAuthInteractor(
+ impl: SystemUIKeyguardFaceAuthInteractor
+ ): KeyguardFaceAuthInteractor
+
@Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ @FaceAuthTableLog
+ fun provideFaceAuthTableLog(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("FaceAuthTableLog", 100)
+ }
+
+ @Provides
+ @SysUISingleton
+ @FaceDetectTableLog
+ fun provideFaceDetectTableLog(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("FaceDetectTableLog", 100)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
new file mode 100644
index 000000000000..06ae11fe810c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Interactor that exposes API to get the face authentication status and handle any events that can
+ * cause face authentication to run.
+ */
+interface KeyguardFaceAuthInteractor {
+
+ /** Current authentication status */
+ val authenticationStatus: Flow<AuthenticationStatus>
+
+ /** Current detection status */
+ val detectionStatus: Flow<DetectionStatus>
+
+ /** Can face auth be run right now */
+ fun canFaceAuthRun(): Boolean
+
+ /** Whether face auth is currently running or not. */
+ fun isRunning(): Boolean
+
+ /** Whether face auth is in lock out state. */
+ fun isLockedOut(): Boolean
+
+ /**
+ * Register listener for use from code that cannot use [authenticationStatus] or
+ * [detectionStatus]
+ */
+ fun registerListener(listener: FaceAuthenticationListener)
+
+ /** Unregister previously registered listener */
+ fun unregisterListener(listener: FaceAuthenticationListener)
+
+ /** Whether the face auth interactor is enabled or not. */
+ fun isEnabled(): Boolean
+
+ fun onUdfpsSensorTouched()
+ fun onAssistantTriggeredOnLockScreen()
+ fun onDeviceLifted()
+ fun onQsExpansionStared()
+ fun onNotificationPanelClicked()
+ fun onSwipeUpOnBouncer()
+}
+
+/**
+ * Listener that can be registered with the [KeyguardFaceAuthInteractor] to receive updates about
+ * face authentication & detection updates.
+ *
+ * This is present to make it easier for use the new face auth API for code that cannot use
+ * [KeyguardFaceAuthInteractor.authenticationStatus] or [KeyguardFaceAuthInteractor.detectionStatus]
+ * flows.
+ */
+interface FaceAuthenticationListener {
+ /** Receive face authentication status updates */
+ fun onAuthenticationStatusChanged(status: AuthenticationStatus)
+
+ /** Receive status updates whenever face detection runs */
+ fun onDetectionStatusChanged(status: DetectionStatus)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index aabd212c1bd3..da0ada160518 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -78,6 +78,14 @@ constructor(
val primaryBouncerToGoneTransition: Flow<TransitionStep> =
repository.transition(PRIMARY_BOUNCER, GONE)
+ /** OFF->LOCKSCREEN transition information. */
+ val offToLockscreenTransition: Flow<TransitionStep> =
+ repository.transition(KeyguardState.OFF, LOCKSCREEN)
+
+ /** DOZING->LOCKSCREEN transition information. */
+ val dozingToLockscreenTransition: Flow<TransitionStep> =
+ repository.transition(KeyguardState.DOZING, LOCKSCREEN)
+
/**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
* Lockscreen (0f).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
new file mode 100644
index 000000000000..cad40aac00d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+/**
+ * Implementation of the interactor that noops all face auth operations.
+ *
+ * This is required for SystemUI variants that do not support face authentication but still inject
+ * other SysUI components that depend on [KeyguardFaceAuthInteractor]
+ */
+@SysUISingleton
+class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
+ override val authenticationStatus: Flow<AuthenticationStatus>
+ get() = emptyFlow()
+ override val detectionStatus: Flow<DetectionStatus>
+ get() = emptyFlow()
+
+ override fun canFaceAuthRun(): Boolean = false
+
+ override fun isRunning(): Boolean = false
+
+ override fun isLockedOut(): Boolean = false
+
+ override fun isEnabled() = false
+
+ override fun registerListener(listener: FaceAuthenticationListener) {}
+
+ override fun unregisterListener(listener: FaceAuthenticationListener) {}
+
+ override fun onUdfpsSensorTouched() {}
+
+ override fun onAssistantTriggeredOnLockScreen() {}
+
+ override fun onDeviceLifted() {}
+
+ override fun onQsExpansionStared() {}
+
+ override fun onNotificationPanelClicked() {}
+
+ override fun onSwipeUpOnBouncer() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
new file mode 100644
index 000000000000..20ebb711c42d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.util.kotlin.pairwise
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+/**
+ * Encapsulates business logic related face authentication being triggered for device entry from
+ * SystemUI Keyguard.
+ */
+@SysUISingleton
+class SystemUIKeyguardFaceAuthInteractor
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ private val repository: DeviceEntryFaceAuthRepository,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val featureFlags: FeatureFlags,
+ private val faceAuthenticationLogger: FaceAuthenticationLogger,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) : CoreStartable, KeyguardFaceAuthInteractor {
+
+ private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
+
+ override fun start() {
+ if (!isEnabled()) {
+ return
+ }
+ // This is required because fingerprint state required for the face auth repository is
+ // backed by KeyguardUpdateMonitor. KeyguardUpdateMonitor constructor accesses the biometric
+ // state which makes lazy injection not an option.
+ keyguardUpdateMonitor.setFaceAuthInteractor(this)
+ observeFaceAuthStateUpdates()
+ faceAuthenticationLogger.interactorStarted()
+ primaryBouncerInteractor.isShowing
+ .whenItFlipsToTrue()
+ .onEach {
+ faceAuthenticationLogger.bouncerVisibilityChanged()
+ runFaceAuth(
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
+ fallbackToDetect = true
+ )
+ }
+ .launchIn(applicationScope)
+
+ alternateBouncerInteractor.isVisible
+ .whenItFlipsToTrue()
+ .onEach {
+ faceAuthenticationLogger.alternateBouncerVisibilityChanged()
+ runFaceAuth(
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
+ fallbackToDetect = false
+ )
+ }
+ .launchIn(applicationScope)
+
+ merge(
+ keyguardTransitionInteractor.aodToLockscreenTransition,
+ keyguardTransitionInteractor.offToLockscreenTransition,
+ keyguardTransitionInteractor.dozingToLockscreenTransition
+ )
+ .filter { it.transitionState == TransitionState.STARTED }
+ .onEach {
+ faceAuthenticationLogger.lockscreenBecameVisible(it)
+ runFaceAuth(
+ FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
+ fallbackToDetect = true
+ )
+ }
+ .launchIn(applicationScope)
+ }
+
+ override fun onSwipeUpOnBouncer() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false)
+ }
+
+ override fun onNotificationPanelClicked() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true)
+ }
+
+ override fun onQsExpansionStared() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)
+ }
+
+ override fun onDeviceLifted() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED, true)
+ }
+
+ override fun onAssistantTriggeredOnLockScreen() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED, true)
+ }
+
+ override fun onUdfpsSensorTouched() {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN, false)
+ }
+
+ override fun registerListener(listener: FaceAuthenticationListener) {
+ listeners.add(listener)
+ }
+
+ override fun unregisterListener(listener: FaceAuthenticationListener) {
+ listeners.remove(listener)
+ }
+
+ override fun isLockedOut(): Boolean = repository.isLockedOut.value
+
+ override fun isRunning(): Boolean = repository.isAuthRunning.value
+
+ override fun canFaceAuthRun(): Boolean = repository.canRunFaceAuth.value
+
+ override fun isEnabled(): Boolean {
+ return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)
+ }
+
+ /** Provide the status of face authentication */
+ override val authenticationStatus = repository.authenticationStatus
+
+ /** Provide the status of face detection */
+ override val detectionStatus = repository.detectionStatus
+
+ private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
+ if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
+ applicationScope.launch {
+ faceAuthenticationLogger.authRequested(uiEvent)
+ repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
+ }
+ } else {
+ faceAuthenticationLogger.ignoredFaceAuthTrigger(
+ uiEvent,
+ ignoredReason = "Skipping face auth request because feature flag is false"
+ )
+ }
+ }
+
+ private fun observeFaceAuthStateUpdates() {
+ authenticationStatus
+ .onEach { authStatusUpdate ->
+ listeners.forEach { it.onAuthenticationStatusChanged(authStatusUpdate) }
+ }
+ .flowOn(mainDispatcher)
+ .launchIn(applicationScope)
+ detectionStatus
+ .onEach { detectionStatusUpdate ->
+ listeners.forEach { it.onDetectionStatusChanged(detectionStatusUpdate) }
+ }
+ .flowOn(mainDispatcher)
+ .launchIn(applicationScope)
+ }
+
+ companion object {
+ const val TAG = "KeyguardFaceAuthInteractor"
+ }
+}
+
+// Extension method that filters a generic Boolean flow to one that emits
+// whenever there is flip from false -> true
+private fun Flow<Boolean>.whenItFlipsToTrue(): Flow<Boolean> {
+ return this.pairwise()
+ .filter { pair -> !pair.previousValue && pair.newValue }
+ .map { it.newValue }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index f7355d5c11e2..7f6e4a903d76 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,6 +4,7 @@ import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.dagger.FaceAuthLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -27,15 +28,15 @@ class FaceAuthenticationLogger
constructor(
@FaceAuthLog private val logBuffer: LogBuffer,
) {
- fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent) {
+ fun ignoredFaceAuthTrigger(uiEvent: FaceAuthUiEvent, ignoredReason: String) {
logBuffer.log(
TAG,
DEBUG,
- { str1 = uiEvent.reason },
{
- "Ignoring trigger because face auth is currently running. " +
- "Trigger reason: $str1"
- }
+ str1 = uiEvent.reason
+ str2 = ignoredReason
+ },
+ { "Ignoring trigger because $str2, Trigger reason: $str1" }
)
}
@@ -135,15 +136,6 @@ constructor(
logBuffer.log(TAG, DEBUG, "Face authentication failed")
}
- fun authenticationAcquired(acquireInfo: Int) {
- logBuffer.log(
- TAG,
- DEBUG,
- { int1 = acquireInfo },
- { "Face acquired during face authentication: acquireInfo: $int1 " }
- )
- }
-
fun authenticationError(
errorCode: Int,
errString: CharSequence?,
@@ -217,4 +209,34 @@ constructor(
fun cancellingFaceAuth() {
logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
}
+
+ fun interactorStarted() {
+ logBuffer.log(TAG, DEBUG, "KeyguardFaceAuthInteractor started")
+ }
+
+ fun bouncerVisibilityChanged() {
+ logBuffer.log(TAG, DEBUG, "Triggering face auth because primary bouncer is visible")
+ }
+
+ fun alternateBouncerVisibilityChanged() {
+ logBuffer.log(TAG, DEBUG, "Triggering face auth because alternate bouncer is visible")
+ }
+
+ fun lockscreenBecameVisible(transitionStep: TransitionStep?) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = "$transitionStep" },
+ { "Triggering face auth because lockscreen became visible due to transition: $str1" }
+ )
+ }
+
+ fun authRequested(uiEvent: FaceAuthUiEvent) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = "$uiEvent" },
+ { "Requesting face auth for trigger: $str1" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
index dbc2a5ec4e0a..b29b5887545a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -19,7 +19,7 @@ package com.android.systemui.media.taptotransfer.common
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
-import androidx.annotation.ColorRes
+import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
@@ -108,7 +108,7 @@ class MediaTttUtils {
data class IconInfo(
val contentDescription: ContentDescription,
val icon: MediaTttIcon,
- @ColorRes val tint: Int?,
+ @AttrRes val tint: Int?,
/**
* True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 26b0e8dd9895..b9ef916eebdf 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -55,6 +55,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -173,7 +174,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
};
-
+ private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
private final Context mContext;
private final UserTracker mUserTracker;
private final OverviewProxyService mOverviewProxyService;
@@ -901,6 +902,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev);
}
+ // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
+ // ACTION_DOWN, in that case we should just reuse the old instance.
+ mVelocityTracker.clear();
+
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
mInputEventReceiver.setBatchingEnabled(false);
@@ -1027,11 +1032,30 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private void dispatchToBackAnimation(MotionEvent event) {
if (mBackAnimation != null) {
+ mVelocityTracker.addMovement(event);
+
+ final float velocityX;
+ final float velocityY;
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ // Compute the current velocity is expensive (see computeCurrentVelocity), so we
+ // are only doing it when the user completes the gesture.
+ int unitPixelPerSecond = 1000;
+ int maxVelocity = mViewConfiguration.getScaledMaximumFlingVelocity();
+ mVelocityTracker.computeCurrentVelocity(unitPixelPerSecond, maxVelocity);
+ velocityX = mVelocityTracker.getXVelocity();
+ velocityY = mVelocityTracker.getYVelocity();
+ } else {
+ velocityX = Float.NaN;
+ velocityY = Float.NaN;
+ }
+
mBackAnimation.onBackMotion(
- event.getX(),
- event.getY(),
- event.getActionMasked(),
- mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
+ /* touchX = */ event.getX(),
+ /* touchY = */ event.getY(),
+ /* velocityX = */ velocityX,
+ /* velocityY = */ velocityY,
+ /* keyAction = */ event.getActionMasked(),
+ /* swipeEdge = */ mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 57b479e6899f..856c64a52290 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -20,8 +20,6 @@ import android.content.res.Resources;
import android.os.Build;
import android.provider.Settings;
-import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
@@ -55,11 +53,9 @@ public interface QSHost {
return tiles;
}
- void warn(String message, Throwable t);
Context getContext();
Context getUserContext();
int getUserId();
- UiEventLogger getUiEventLogger();
Collection<QSTile> getTiles();
void addCallback(Callback callback);
void removeCallback(Callback callback);
@@ -107,8 +103,6 @@ public interface QSHost {
int indexOf(String tileSpec);
- InstanceId getNewInstanceId();
-
interface Callback {
void onTilesChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 14acb4b3ccf4..67927a4153b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -19,8 +19,6 @@ package com.android.systemui.qs
import android.content.ComponentName
import android.content.Context
import androidx.annotation.GuardedBy
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
@@ -39,10 +37,9 @@ import kotlinx.coroutines.launch
/**
* Adapter to determine what real class to use for classes that depend on [QSHost].
- *
* * When [Flags.QS_PIPELINE_NEW_HOST] is off, all calls will be routed to [QSTileHost].
* * When [Flags.QS_PIPELINE_NEW_HOST] is on, calls regarding the current set of tiles will be
- * routed to [CurrentTilesInteractor]. Other calls (like [warn]) will still be routed to
+ * routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will still be routed to
* [QSTileHost].
*
* This routing also includes dumps.
@@ -71,10 +68,7 @@ constructor(
init {
scope.launch { tileServiceRequestControllerBuilder.create(this@QSHostAdapter).init() }
// Redirect dump to the correct host (needed for CTS tests)
- dumpManager.registerCriticalDumpable(
- TAG,
- if (useNewHost) interactor else qsTileHost
- )
+ dumpManager.registerCriticalDumpable(TAG, if (useNewHost) interactor else qsTileHost)
}
override fun getTiles(): Collection<QSTile> {
@@ -103,10 +97,7 @@ constructor(
override fun addCallback(callback: QSHost.Callback) {
if (useNewHost) {
- val job =
- scope.launch {
- interactor.currentTiles.collect { callback.onTilesChanged() }
- }
+ val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } }
synchronized(callbacksMap) { callbacksMap.put(callback, job) }
} else {
qsTileHost.addCallback(callback)
@@ -147,10 +138,7 @@ constructor(
override fun addTile(component: ComponentName, end: Boolean) {
if (useNewHost) {
- interactor.addTile(
- TileSpec.create(component),
- if (end) POSITION_AT_END else 0
- )
+ interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0)
} else {
qsTileHost.addTile(component, end)
}
@@ -164,10 +152,6 @@ constructor(
}
}
- override fun warn(message: String?, t: Throwable?) {
- qsTileHost.warn(message, t)
- }
-
override fun getContext(): Context {
return if (useNewHost) {
context
@@ -192,10 +176,6 @@ constructor(
}
}
- override fun getUiEventLogger(): UiEventLogger {
- return qsTileHost.uiEventLogger
- }
-
override fun createTileView(
themedContext: Context?,
tile: QSTile?,
@@ -219,8 +199,4 @@ constructor(
override fun indexOf(tileSpec: String): Int {
return specs.indexOf(tileSpec)
}
-
- override fun getNewInstanceId(): InstanceId {
- return qsTileHost.newInstanceId
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 0ca897373d13..59b94b7c4bd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -29,9 +29,6 @@ import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.ProtoDumpable;
import com.android.systemui.R;
@@ -91,7 +88,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
PanelInteractor, CustomTileAddedRepository {
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int MAX_QS_INSTANCE_ID = 1 << 20;
// Shared prefs that hold tile lifecycle info.
@VisibleForTesting
@@ -103,8 +99,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
private final TunerService mTunerService;
private final PluginManager mPluginManager;
private final QSLogger mQSLogger;
- private final UiEventLogger mUiEventLogger;
- private final InstanceIdSequence mInstanceIdSequence;
private final CustomTileStatePersister mCustomTileStatePersister;
private final Executor mMainExecutor;
private final UserFileManager mUserFileManager;
@@ -137,7 +131,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
Provider<AutoTileManager> autoTiles,
Optional<CentralSurfaces> centralSurfacesOptional,
QSLogger qsLogger,
- UiEventLogger uiEventLogger,
UserTracker userTracker,
SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
@@ -150,13 +143,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
mTunerService = tunerService;
mPluginManager = pluginManager;
mQSLogger = qsLogger;
- mUiEventLogger = uiEventLogger;
mMainExecutor = mainExecutor;
mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
mUserFileManager = userFileManager;
mFeatureFlags = featureFlags;
- mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
mCentralSurfacesOptional = centralSurfacesOptional;
mQsFactories.add(defaultFactory);
@@ -175,11 +166,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
});
}
- @Override
- public InstanceId getNewInstanceId() {
- return mInstanceIdSequence.newInstanceId();
- }
-
public void destroy() {
mTiles.values().forEach(tile -> tile.destroy());
mAutoTiles.destroy();
@@ -207,11 +193,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
}
@Override
- public UiEventLogger getUiEventLogger() {
- return mUiEventLogger;
- }
-
- @Override
public void addCallback(Callback callback) {
mCallbacks.add(callback);
}
@@ -227,11 +208,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
}
@Override
- public void warn(String message, Throwable t) {
- // already logged
- }
-
- @Override
public void collapsePanels() {
mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateCollapsePanels);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt
new file mode 100644
index 000000000000..fc739edc69d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QsEventLogger.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+interface QsEventLogger : UiEventLogger {
+ fun getNewInstanceId(): InstanceId
+}
+
+@SysUISingleton
+class QsEventLoggerImpl
+@Inject
+constructor(
+ uiEventLogger: UiEventLogger,
+) : QsEventLogger, UiEventLogger by uiEventLogger {
+
+ companion object {
+ private const val MAX_QS_INSTANCE_ID = 1 shl 20
+ }
+
+ val sequence = InstanceIdSequence(MAX_QS_INSTANCE_ID)
+ override fun getNewInstanceId(): InstanceId {
+ return sequence.newInstanceId()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 3ddd9f1cfe5f..1f63f5da2f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -21,6 +21,8 @@ import com.android.systemui.flags.Flags
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QSHostAdapter
import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.QsEventLoggerImpl
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
@@ -34,8 +36,12 @@ interface QSHostModule {
@Binds fun provideQsHost(controllerImpl: QSHostAdapter): QSHost
+ @Binds fun provideEventLogger(impl: QsEventLoggerImpl): QsEventLogger
+
@Module
companion object {
+ private const val MAX_QS_INSTANCE_ID = 1 shl 20
+
@Provides
@JvmStatic
fun providePanelInteractor(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index d4854e1a7daf..897b0e73dca0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -59,17 +59,20 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.DisplayTracker;
+import dagger.Lazy;
+
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
-import dagger.Lazy;
+
public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
public static final String PREFIX = "custom(";
@@ -111,6 +114,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
private CustomTile(
QSHost host,
+ QsEventLogger uiEventLogger,
Looper backgroundLooper,
Handler mainHandler,
FalsingManager falsingManager,
@@ -124,7 +128,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
TileServices tileServices,
DisplayTracker displayTracker
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mTileServices = tileServices;
mWindowManager = WindowManagerGlobal.getWindowManagerService();
@@ -561,6 +565,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
public static class Builder {
final Lazy<QSHost> mQSHostLazy;
+ final QsEventLogger mUiEventLogger;
final Looper mBackgroundLooper;
final Handler mMainHandler;
private final FalsingManager mFalsingManager;
@@ -578,6 +583,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
@Inject
public Builder(
Lazy<QSHost> hostLazy,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -590,6 +596,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
DisplayTracker displayTracker
) {
mQSHostLazy = hostLazy;
+ mUiEventLogger = uiEventLogger;
mBackgroundLooper = backgroundLooper;
mMainHandler = mainHandler;
mFalsingManager = falsingManager;
@@ -620,6 +627,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
String action = getAction(mSpec);
return new CustomTile(
mQSHostLazy.get(),
+ mUiEventLogger,
mBackgroundLooper,
mMainHandler,
mFalsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 49ba5086f64d..2a9e7d05c187 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -66,6 +66,7 @@ import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SideLabelTileLayout;
import com.android.systemui.qs.logging.QSLogger;
@@ -179,6 +180,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
protected QSTileImpl(
QSHost host,
+ QsEventLogger uiEventLogger,
Looper backgroundLooper,
Handler mainHandler,
FalsingManager falsingManager,
@@ -189,8 +191,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
) {
mHost = host;
mContext = host.getContext();
- mInstanceId = host.getNewInstanceId();
- mUiEventLogger = host.getUiEventLogger();
+ mInstanceId = uiEventLogger.getNewInstanceId();
+ mUiEventLogger = uiEventLogger;
mUiHandler = mainHandler;
mHandler = new H(backgroundLooper);
@@ -633,7 +635,6 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
} catch (Throwable t) {
final String error = "Error in " + name;
Log.w(TAG, error, t);
- mHost.warn(error, t);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 92a83bba8a68..30765f7f974d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -45,15 +45,18 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.GlobalSettings;
+import dagger.Lazy;
+
import javax.inject.Inject;
-import dagger.Lazy;
+
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
@@ -69,6 +72,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
@Inject
public AirplaneModeTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -81,7 +85,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
GlobalSettings globalSettings,
UserTracker userTracker
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
mLazyConnectivityManager = lazyConnectivityManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 2ca452e45ecf..c709969ec6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -22,6 +22,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.settings.UserTracker
@@ -31,6 +32,7 @@ import javax.inject.Inject
class AlarmTile @Inject constructor(
host: QSHost,
+ uiEventLogger: QsEventLogger,
@Background backgroundLooper: Looper,
@Main mainHandler: Handler,
falsingManager: FalsingManager,
@@ -42,6 +44,7 @@ class AlarmTile @Inject constructor(
nextAlarmController: NextAlarmController
) : QSTileImpl<QSTile.State>(
host,
+ uiEventLogger,
backgroundLooper,
mainHandler,
falsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 027a464251c9..a444e7631527 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -62,6 +63,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
@Inject
public BatterySaverTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -72,7 +74,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
BatteryController batteryController,
SecureSettings secureSettings
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 08fe2709b810..30218a6a3180 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -47,6 +47,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
@@ -74,6 +75,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
@Inject
public BluetoothTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -83,7 +85,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
QSLogger qsLogger,
BluetoothController bluetoothController
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = bluetoothController;
mController.observe(getLifecycle(), mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 93e5f1efbdc8..65ef6b9c31a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +49,9 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
public static final String TILE_SPEC = "cameratoggle";
@Inject
- protected CameraToggleTile(QSHost host,
+ protected CameraToggleTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
MetricsLogger metricsLogger,
@@ -58,7 +61,7 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
QSLogger qsLogger,
IndividualSensorPrivacyController sensorPrivacyController,
KeyguardStateController keyguardStateController) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
keyguardStateController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 8d984817ba77..54376fe604df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -47,6 +47,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -84,6 +85,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
@Inject
public CastTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -97,7 +99,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
HotspotController hotspotController,
DialogLaunchAnimator dialogLaunchAnimator
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = castController;
mKeyguard = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index b6205d5df63d..cf9e3468c8f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -56,6 +57,7 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> {
@Inject
public ColorCorrectionTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -66,7 +68,7 @@ public class ColorCorrectionTile extends QSTileImpl<BooleanState> {
UserTracker userTracker,
SecureSettings secureSettings
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mSetting = new SettingObserver(secureSettings, mHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 9a44e83ad9a1..4ecde6123dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -55,6 +56,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
@Inject
public ColorInversionTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -65,7 +67,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
UserTracker userTracker,
SecureSettings secureSettings
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mSetting = new SettingObserver(secureSettings, mHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index add517e18516..e769b5ef9989 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -58,6 +59,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
@Inject
public DataSaverTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -68,7 +70,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
DataSaverController dataSaverController,
DialogLaunchAnimator dialogLaunchAnimator
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mDataSaverController = dataSaverController;
mDialogLaunchAnimator = dialogLaunchAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 01164fb0a009..ddaff3bb9a64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -40,6 +40,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import java.util.concurrent.atomic.AtomicBoolean
@@ -47,6 +48,7 @@ import javax.inject.Inject
class DeviceControlsTile @Inject constructor(
host: QSHost,
+ uiEventLogger: QsEventLogger,
@Background backgroundLooper: Looper,
@Main mainHandler: Handler,
falsingManager: FalsingManager,
@@ -56,14 +58,15 @@ class DeviceControlsTile @Inject constructor(
qsLogger: QSLogger,
private val controlsComponent: ControlsComponent
) : QSTileImpl<QSTile.State>(
- host,
- backgroundLooper,
- mainHandler,
- falsingManager,
- metricsLogger,
- statusBarStateController,
- activityStarter,
- qsLogger
+ host,
+ uiEventLogger,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
) {
private var hasControlsApps = AtomicBoolean(false)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 434fe45f47c0..3e7bdd1a1444 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -54,6 +54,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -88,6 +89,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
@Inject
public DndTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -100,7 +102,7 @@ public class DndTile extends QSTileImpl<BooleanState> {
SecureSettings secureSettings,
DialogLaunchAnimator dialogLaunchAnimator
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = zenModeController;
mSharedPreferences = sharedPreferences;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index f913326a6a67..eef4c1dd4436 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -48,6 +48,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -90,6 +91,7 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> {
@Inject
public DreamTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -105,7 +107,7 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> {
@Named(DreamModule.DREAM_ONLY_ENABLED_FOR_DOCK_USER)
boolean dreamOnlyEnabledForDockUser
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mDreamManager = dreamManager;
mBroadcastDispatcher = broadcastDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index e091a750fbec..2c986da8370a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -55,6 +56,7 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
@Inject
public FlashlightTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -64,7 +66,7 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
QSLogger qsLogger,
FlashlightController flashlightController
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mFlashlightController = flashlightController;
mFlashlightController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 3f514344cee1..12d98473ff07 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -36,6 +36,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -47,6 +48,7 @@ class FontScalingTile
@Inject
constructor(
host: QSHost,
+ uiEventLogger: QsEventLogger,
@Background backgroundLooper: Looper,
@Main mainHandler: Handler,
falsingManager: FalsingManager,
@@ -61,6 +63,7 @@ constructor(
) :
QSTileImpl<QSTile.State?>(
host,
+ uiEventLogger,
backgroundLooper,
mainHandler,
falsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 6bf8b7666054..4c3699ced6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -41,6 +41,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -61,6 +62,7 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
@Inject
public HotspotTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -71,7 +73,7 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
HotspotController hotspotController,
DataSaverController dataSaverController
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
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 75d01723667d..f16f0dcc5dba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -51,6 +51,7 @@ import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
@@ -90,6 +91,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Inject
public InternetTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -101,7 +103,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
AccessPointController accessPointController,
InternetDialogFactory internetDialogFactory
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mInternetDialogFactory = internetDialogFactory;
mHandler = mainHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 27f58269722a..83c568878c94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -59,6 +60,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
@Inject
public LocationTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -70,7 +72,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
KeyguardStateController keyguardStateController,
PanelInteractor panelInteractor
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = locationController;
mKeyguard = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 2e475d40d55f..86a6a8c6a2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +49,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
public static final String TILE_SPEC = "mictoggle";
@Inject
- protected MicrophoneToggleTile(QSHost host,
+ protected MicrophoneToggleTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
MetricsLogger metricsLogger,
@@ -58,7 +61,7 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
QSLogger qsLogger,
IndividualSensorPrivacyController sensorPrivacyController,
KeyguardStateController keyguardStateController) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
keyguardStateController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index e189f80a7c23..29ccb76de820 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -43,6 +43,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -65,6 +66,7 @@ public class NfcTile extends QSTileImpl<BooleanState> {
@Inject
public NfcTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -74,7 +76,7 @@ public class NfcTile extends QSTileImpl<BooleanState> {
QSLogger qsLogger,
BroadcastDispatcher broadcastDispatcher
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mBroadcastDispatcher = broadcastDispatcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index aacd53bde51c..405e1394871c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -45,6 +45,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.LocationController;
@@ -80,6 +81,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
@Inject
public NightDisplayTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -91,7 +93,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
ColorDisplayManager colorDisplayManager,
NightDisplayListenerModule.Builder nightDisplayListenerBuilder
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mLocationController = locationController;
mManager = colorDisplayManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index ae67d99ea2fb..1eb317aaeae8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -36,6 +36,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -57,6 +58,7 @@ public class OneHandedModeTile extends QSTileImpl<BooleanState> {
@Inject
public OneHandedModeTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -66,7 +68,7 @@ public class OneHandedModeTile extends QSTileImpl<BooleanState> {
QSLogger qsLogger,
UserTracker userTracker,
SecureSettings secureSettings) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mSetting = new SettingObserver(secureSettings, mHandler,
Settings.Secure.ONE_HANDED_MODE_ENABLED, userTracker.getUserId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 92f52724ca0c..9e365d34bed9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -62,6 +63,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
@Inject
public QRCodeScannerTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -70,7 +72,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
ActivityStarter activityStarter,
QSLogger qsLogger,
QRCodeScannerController qrCodeScannerController) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mQRCodeScannerController = qrCodeScannerController;
mQRCodeScannerController.observe(getLifecycle(), mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 4a3c56328006..e026bdbc2e08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -49,6 +49,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -83,6 +84,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
@Inject
public QuickAccessWalletTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -94,7 +96,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
PackageManager packageManager,
SecureSettings secureSettings,
QuickAccessWalletController quickAccessWalletController) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = quickAccessWalletController;
mKeyguardStateController = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 10f1ce4946c8..2e04afb0048a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -59,6 +60,7 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
@Named(RBC_AVAILABLE) boolean isAvailable,
ReduceBrightColorsController reduceBrightColorsController,
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -67,7 +69,7 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
ActivityStarter activityStarter,
QSLogger qsLogger
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mReduceBrightColorsController = reduceBrightColorsController;
mReduceBrightColorsController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 8888c733c3c1..7f7f8ad6a4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -44,6 +44,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -71,6 +72,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements
@Inject
public RotationLockTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -83,7 +85,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements
BatteryController batteryController,
SecureSettings secureSettings
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = rotationLockController;
mController.observe(this, mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 65592a717565..2d4652db6b80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -41,6 +41,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -74,6 +75,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
@Inject
public ScreenRecordTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -88,7 +90,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
DialogLaunchAnimator dialogLaunchAnimator,
PanelInteractor panelInteractor
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = controller;
mController.observe(this, mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index d99c1d1daf7e..7c4f097a1724 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
@@ -68,7 +69,9 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS
*/
public abstract String getRestriction();
- protected SensorPrivacyToggleTile(QSHost host,
+ protected SensorPrivacyToggleTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -78,7 +81,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS
QSLogger qsLogger,
IndividualSensorPrivacyController sensorPrivacyController,
KeyguardStateController keyguardStateController) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mSensorPrivacyController = sensorPrivacyController;
mKeyguard = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 809689cb806d..a60d1ad448b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -69,6 +70,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
@Inject
public UiModeNightTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -80,7 +82,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
BatteryController batteryController,
LocationController locationController
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mBatteryController = batteryController;
mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 6a5c99032457..17e72e597e58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -40,6 +40,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -59,6 +60,7 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
@Inject
public WorkModeTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -68,7 +70,7 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
QSLogger qsLogger,
ManagedProfileController managedProfileController
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mProfileController = managedProfileController;
mProfileController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index aedd9762a601..222a0f4fa248 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3333,7 +3333,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mGestureRecorder = recorder;
mHideExpandedRunnable = hideExpandedRunnable;
- mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mNotificationStackScrollLayoutController.setShelfController(
+ notificationShelfController);
+ }
mNotificationShelfController = notificationShelfController;
mLockscreenShadeTransitionController.bindController(notificationShelfController);
updateMaxDisplayedNotifications(true);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 7cb1cbe77539..ef14d1cb7f63 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -64,6 +64,7 @@ import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
@@ -132,6 +133,7 @@ public class QuickSettingsController {
private final FalsingCollector mFalsingCollector;
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final ShadeLogger mShadeLog;
+ private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
private final FalsingManager mFalsingManager;
@@ -318,7 +320,8 @@ public class QuickSettingsController {
MetricsLogger metricsLogger,
FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
- ShadeLogger shadeLog
+ ShadeLogger shadeLog,
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor
) {
mPanelViewControllerLazy = panelViewControllerLazy;
mPanelView = panelView;
@@ -357,6 +360,7 @@ public class QuickSettingsController {
mLockscreenGestureLogger = lockscreenGestureLogger;
mMetricsLogger = metricsLogger;
mShadeLog = shadeLog;
+ mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
mFeatureFlags = featureFlags;
mInteractionJankMonitor = interactionJankMonitor;
@@ -937,6 +941,7 @@ public class QuickSettingsController {
// When expanding QS, let's authenticate the user if possible,
// this will speed up notification actions.
if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
+ mKeyguardFaceAuthInteractor.onQsExpansionStared();
mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.QS_EXPANDED);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d1c6aef7b306..7eb63da38028 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -53,6 +53,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -99,6 +101,9 @@ public class NotificationShelf extends ActivatableNotificationView implements St
private boolean mSensitiveRevealAnimEndabled;
private boolean mShelfRefactorFlagEnabled;
private boolean mCanModifyColorOfNotifications;
+ private boolean mCanInteract;
+ private NotificationStackScrollLayout mHostLayout;
+ private NotificationRoundnessManager mRoundnessManager;
public NotificationShelf(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -135,6 +140,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) {
+ assertRefactorFlagDisabled();
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -142,6 +148,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St
});
}
+ public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
+ NotificationRoundnessManager roundnessManager) {
+ if (!checkRefactorFlagEnabled()) return;
+ mAmbientState = ambientState;
+ mHostLayout = hostLayout;
+ mRoundnessManager = roundnessManager;
+ }
+
private void updateResources() {
Resources res = getResources();
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
@@ -233,7 +247,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
} else {
viewState.setAlpha(1f - ambientState.getHideAmount());
}
- viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
+ viewState.belowSpeedBump = getSpeedBumpIndex() == 0;
viewState.hideSensitive = false;
viewState.setXTranslation(getTranslationX());
viewState.hasItemsInStableShelf = lastViewState.inShelf;
@@ -276,6 +290,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
}
+ private int getSpeedBumpIndex() {
+ if (mShelfRefactorFlagEnabled) {
+ return mHostLayout.getSpeedBumpIndex();
+ } else {
+ return mHostLayoutController.getSpeedBumpIndex();
+ }
+ }
+
/**
* @param fractionToShade Fraction of lockscreen to shade transition
* @param shortestWidth Shortest width to use for lockscreen shelf
@@ -388,8 +410,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St
int baseZHeight = mAmbientState.getBaseZHeight();
int clipTopAmount = 0;
- for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
- ExpandableView child = mHostLayoutController.getChildAt(i);
+ for (int i = 0; i < getHostLayoutChildCount(); i++) {
+ ExpandableView child = getHostLayoutChildAt(i);
if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
continue;
}
@@ -474,11 +496,11 @@ public class NotificationShelf extends ActivatableNotificationView implements St
// TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
- mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
+ mShelfIcons.setSpeedBumpIndex(getSpeedBumpIndex());
mShelfIcons.calculateIconXTranslations();
mShelfIcons.applyIconStates();
- for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
- View child = mHostLayoutController.getChildAt(i);
+ for (int i = 0; i < getHostLayoutChildCount(); i++) {
+ View child = getHostLayoutChildAt(i);
if (!(child instanceof ExpandableNotificationRow)
|| child.getVisibility() == GONE) {
continue;
@@ -493,6 +515,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
}
+ private ExpandableView getHostLayoutChildAt(int index) {
+ if (mShelfRefactorFlagEnabled) {
+ return (ExpandableView) mHostLayout.getChildAt(index);
+ } else {
+ return mHostLayoutController.getChildAt(index);
+ }
+ }
+
+ private int getHostLayoutChildCount() {
+ if (mShelfRefactorFlagEnabled) {
+ return mHostLayout.getChildCount();
+ } else {
+ return mHostLayoutController.getChildCount();
+ }
+ }
+
private boolean canModifyColorOfNotifications() {
if (mShelfRefactorFlagEnabled) {
return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded();
@@ -515,7 +553,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
&& anv == mAmbientState.getTrackedHeadsUpRow();
final boolean shouldUpdateCornerRoundness = viewStart < shelfStart
- && !mHostLayoutController.isViewAffectedBySwipe(anv)
+ && !isViewAffectedBySwipe(anv)
&& !isUnlockedHeadsUp
&& !isHunGoingToShade
&& !anv.isAboveShelf()
@@ -567,6 +605,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St
anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false);
}
+ private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
+ if (!mShelfRefactorFlagEnabled) {
+ return mHostLayoutController.isViewAffectedBySwipe(expandableView);
+ } else {
+ return mRoundnessManager.isViewAffectedBySwipe(expandableView);
+ }
+ }
+
/**
* Clips transient views to the top of the shelf - Transient views are only used for
* disappearing views/animations and need to be clipped correctly by the shelf to ensure they
@@ -574,8 +620,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St
* swipes quickly.
*/
private void clipTransientViews() {
- for (int i = 0; i < mHostLayoutController.getTransientViewCount(); i++) {
- View transientView = mHostLayoutController.getTransientView(i);
+ for (int i = 0; i < getHostLayoutTransientViewCount(); i++) {
+ View transientView = getHostLayoutTransientView(i);
if (transientView instanceof ExpandableView) {
ExpandableView transientExpandableView = (ExpandableView) transientView;
updateNotificationClipHeight(transientExpandableView, getTranslationY(), -1);
@@ -583,6 +629,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
}
+ private View getHostLayoutTransientView(int index) {
+ if (mShelfRefactorFlagEnabled) {
+ return mHostLayout.getTransientView(index);
+ } else {
+ return mHostLayoutController.getTransientView(index);
+ }
+ }
+
+ private int getHostLayoutTransientViewCount() {
+ if (mShelfRefactorFlagEnabled) {
+ return mHostLayout.getTransientViewCount();
+ } else {
+ return mHostLayoutController.getTransientViewCount();
+ }
+ }
+
private void updateIconClipAmount(ExpandableNotificationRow row) {
float maxTop = row.getTranslationY();
if (getClipTopAmount() != 0) {
@@ -922,18 +984,27 @@ public class NotificationShelf extends ActivatableNotificationView implements St
@Override
public void onStateChanged(int newState) {
+ assertRefactorFlagDisabled();
mStatusBarState = newState;
updateInteractiveness();
}
private void updateInteractiveness() {
- mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf;
+ mInteractive = canInteract() && mHasItemsInStableShelf;
setClickable(mInteractive);
setFocusable(mInteractive);
setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
: View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
+ private boolean canInteract() {
+ if (mShelfRefactorFlagEnabled) {
+ return mCanInteract;
+ } else {
+ return mStatusBarState == StatusBarState.KEYGUARD;
+ }
+ }
+
@Override
protected boolean isInteractive() {
return mInteractive;
@@ -972,8 +1043,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
private void assertRefactorFlagDisabled() {
if (mShelfRefactorFlagEnabled) {
- throw new IllegalStateException(
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled.");
+ NotificationShelfController.throwIllegalFlagStateError(false);
}
}
@@ -995,8 +1065,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St
mCanModifyColorOfNotifications = canModifyColorOfNotifications;
}
+ public void setCanInteract(boolean canInteract) {
+ if (!checkRefactorFlagEnabled()) return;
+ mCanInteract = canInteract;
+ updateInteractiveness();
+ }
+
public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) {
- mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
+ mIndexOfFirstViewInShelf = getIndexOfViewInHostLayout(firstViewInShelf);
+ }
+
+ private int getIndexOfViewInHostLayout(ExpandableView child) {
+ if (mShelfRefactorFlagEnabled) {
+ return mHostLayout.indexOfChild(child);
+ } else {
+ return mHostLayoutController.indexOfChild(child);
+ }
}
/**
@@ -1011,6 +1095,11 @@ public class NotificationShelf extends ActivatableNotificationView implements St
mShelfRefactorFlagEnabled = enabled;
}
+ public void requestRoundnessResetFor(ExpandableView child) {
+ if (!checkRefactorFlagEnabled()) return;
+ child.requestRoundnessReset(SHELF_SCROLL);
+ }
+
/**
* This method resets the OnScroll roundness of a view to 0f
* <p>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index bf3d47c4a9ca..07cfd0d8019c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar
+import android.util.Log
import android.view.View
import android.view.View.OnClickListener
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView.OnActivatedListener
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -51,4 +54,29 @@ interface NotificationShelfController {
/** @see View.setOnClickListener */
fun setOnClickListener(listener: OnClickListener)
+
+ companion object {
+ @JvmStatic
+ fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) {
+ if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ throwIllegalFlagStateError(expected = false)
+ }
+ }
+
+ @JvmStatic
+ fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean =
+ featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled ->
+ if (!enabled) {
+ Log.wtf("NotifShelf", getErrorMessage(expected = true))
+ }
+ }
+
+ @JvmStatic
+ fun throwIllegalFlagStateError(expected: Boolean): Nothing =
+ error(getErrorMessage(expected))
+
+ private fun getErrorMessage(expected: Boolean): String =
+ "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " +
+ if (expected) "disabled" else "enabled"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
index ecd0c41ff82d..fcff4376811e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java
@@ -48,6 +48,10 @@ public abstract class AnimatableProperty {
View.SCALE_Y, R.id.scale_y_animator_tag, R.id.scale_y_animator_start_value_tag,
R.id.scale_y_animator_end_value_tag);
+ public static final AnimatableProperty ALPHA = AnimatableProperty.from(
+ View.ALPHA, R.id.alpha_animator_tag, R.id.alpha_animator_start_value_tag,
+ R.id.alpha_animator_end_value_tag);
+
/**
* Similar to X, however this doesn't allow for any other modifications other than from this
* property. When using X, it's possible that the view is laid out during the animation,
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 a9d125508397..950ab5d2f5ef 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
@@ -299,6 +299,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
private boolean mShowGroupBackgroundWhenExpanded;
+ /**
+ * True if we always show the collapsed layout on lockscreen because vertical space is low.
+ */
+ private boolean mSaveSpaceOnLockscreen;
+
+ /**
+ * True if we use intrinsic height regardless of vertical space available on lockscreen.
+ */
+ private boolean mIgnoreLockscreenConstraints;
+
private OnClickListener mExpandClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
@@ -394,6 +404,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mGroupExpansionChanging;
}
+ public void setSaveSpaceOnLockscreen(boolean saveSpace) {
+ mSaveSpaceOnLockscreen = saveSpace;
+ }
+
+ public boolean getSaveSpaceOnLockscreen() {
+ return mSaveSpaceOnLockscreen;
+ }
+
public void setGroupExpansionChanging(boolean changing) {
mGroupExpansionChanging = changing;
}
@@ -419,15 +437,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void setAnimationRunning(boolean running) {
// Sets animations running in the private/public layouts.
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE)) {
- for (NotificationContentView l : mLayouts) {
- if (l != null) {
- l.setContentAnimationRunning(running);
- setIconAnimationRunning(running, l);
- }
- }
- } else {
- for (NotificationContentView l : mLayouts) {
+ for (NotificationContentView l : mLayouts) {
+ if (l != null) {
+ l.setContentAnimationRunning(running);
setIconAnimationRunning(running, l);
}
}
@@ -2553,11 +2565,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public int getHeightWithoutLockscreenConstraints() {
+ mIgnoreLockscreenConstraints = true;
+ final int height = getIntrinsicHeight();
+ mIgnoreLockscreenConstraints = false;
+ return height;
+ }
+
+ @Override
public int getIntrinsicHeight() {
if (isUserLocked()) {
return getActualHeight();
- }
- if (mGuts != null && mGuts.isExposed()) {
+ } else if (mGuts != null && mGuts.isExposed()) {
return mGuts.getIntrinsicHeight();
} else if ((isChildInGroup() && !isGroupExpanded())) {
return mPrivateLayout.getMinHeight();
@@ -2579,13 +2598,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return getCollapsedHeight();
}
}
-
/**
* @return {@code true} if the notification can show it's heads up layout. This is mostly true
* except for legacy use cases.
*/
public boolean canShowHeadsUp() {
- if (mOnKeyguard && !isDozing() && !isBypassEnabled() && !mEntry.isStickyAndNotDemoted()) {
+ if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
+ (!mEntry.isStickyAndNotDemoted()
+ || (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
return false;
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 9df6ba9910cc..5edff5f4e5d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -291,6 +291,11 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
long duration) {
}
+ public int getHeightWithoutLockscreenConstraints() {
+ // ExpandableNotificationRow overrides this.
+ return getHeight();
+ }
+
/**
* @return The desired notification height.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index db550c00b4a1..8ba65f7a1418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -32,6 +32,10 @@ constructor(
private val keyguardRepository: KeyguardRepository,
private val deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository,
) {
+ /** Is the shelf showing on the keyguard? */
+ val isShowingOnKeyguard: Flow<Boolean>
+ get() = keyguardRepository.isKeyguardShowing
+
/** Is the system in a state where the shelf is just a static display of notification icons? */
val isShelfStatic: Flow<Boolean>
get() =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index bd531caccc5d..b190cf658fa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
import android.view.View
-import android.view.View.OnAttachStateChangeListener
import android.view.accessibility.AccessibilityManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -29,7 +28,6 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.NotificationShelfController
-import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController
import com.android.systemui.statusbar.notification.row.ExpandableOutlineViewController
@@ -40,9 +38,11 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.NotificationTapHelper
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
+import com.android.systemui.util.kotlin.getValue
+import dagger.Lazy
+import javax.inject.Inject
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import javax.inject.Inject
/**
* Controller class for [NotificationShelf]. This implementation serves as a temporary wrapper
@@ -61,11 +61,13 @@ constructor(
private val a11yManager: AccessibilityManager,
private val falsingManager: FalsingManager,
private val falsingCollector: FalsingCollector,
- private val statusBarStateController: SysuiStatusBarStateController,
+ hostControllerLazy: Lazy<NotificationStackScrollLayoutController>,
) : NotificationShelfController {
+ private val hostController: NotificationStackScrollLayoutController by hostControllerLazy
+
override val view: NotificationShelf
- get() = shelf
+ get() = unsupported
init {
shelf.apply {
@@ -87,22 +89,9 @@ constructor(
falsingCollector,
)
.init()
- val onAttachStateListener =
- object : OnAttachStateChangeListener {
- override fun onViewAttachedToWindow(v: View) {
- statusBarStateController.addCallback(
- shelf,
- SysuiStatusBarStateController.RANK_SHELF,
- )
- }
-
- override fun onViewDetachedFromWindow(v: View) {
- statusBarStateController.removeCallback(shelf)
- }
- }
- shelf.addOnAttachStateChangeListener(onAttachStateListener)
- if (shelf.isAttachedToWindow) {
- onAttachStateListener.onViewAttachedToWindow(shelf)
+ hostController.setShelf(shelf)
+ hostController.setOnNotificationRemovedListener { child, _ ->
+ view.requestRoundnessResetFor(child)
}
}
@@ -121,16 +110,14 @@ constructor(
override fun bind(
ambientState: AmbientState,
notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
- ) {
- shelf.bind(ambientState, notificationStackScrollLayoutController)
- }
+ ) = unsupported
override fun setOnClickListener(listener: View.OnClickListener) {
shelf.setOnClickListener(listener)
}
private val unsupported: Nothing
- get() = error("Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled")
+ get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
}
/** Binds a [NotificationShelf] to its backend. */
@@ -141,6 +128,7 @@ object NotificationShelfViewBinder {
viewModel.canModifyColorOfNotifications
.onEach(shelf::setCanModifyColorOfNotifications)
.launchIn(this)
+ viewModel.isClickable.onEach(shelf::setCanInteract).launchIn(this)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index b84834adf122..5e297c89dd2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -30,6 +30,10 @@ class NotificationShelfViewModel
constructor(
private val interactor: NotificationShelfInteractor,
) {
+ /** Is the shelf allowed to be clickable when it has content? */
+ val isClickable: Flow<Boolean>
+ get() = interactor.isShowingOnKeyguard
+
/** Is the shelf allowed to modify the color of notifications in the host layout? */
val canModifyColorOfNotifications: Flow<Boolean>
get() = interactor.isShelfStatic.map { static -> !static }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 112d48c115c2..00b9aa42ab26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -32,6 +32,7 @@ public class AnimationProperties {
public long duration;
public long delay;
private ArrayMap<Property, Interpolator> mInterpolatorMap;
+ private Consumer<Property> mAnimationCancelAction;
private Consumer<Property> mAnimationEndAction;
/**
@@ -50,27 +51,43 @@ public class AnimationProperties {
* @return a listener that will be added for a given property during its animation.
*/
public AnimatorListenerAdapter getAnimationFinishListener(Property property) {
- if (mAnimationEndAction == null) {
+ if (mAnimationEndAction == null && mAnimationCancelAction == null) {
return null;
}
+ Consumer<Property> cancelAction = mAnimationCancelAction;
Consumer<Property> endAction = mAnimationEndAction;
return new AnimatorListenerAdapter() {
private boolean mCancelled;
@Override
public void onAnimationCancel(Animator animation) {
- mCancelled = true;
+ mCancelled = true;
+ if (cancelAction != null) {
+ cancelAction.accept(property);
+ }
}
@Override
public void onAnimationEnd(Animator animation) {
- if (!mCancelled) {
+ if (!mCancelled && endAction != null) {
endAction.accept(property);
}
}
};
}
+ /**
+ * Add a callback for animation cancellation.
+ */
+ public AnimationProperties setAnimationCancelAction(Consumer<Property> listener) {
+ mAnimationCancelAction = listener;
+ return this;
+ }
+
+ /**
+ * Add a callback for animation ending successfully. The callback will not be called when the
+ * animations is cancelled.
+ */
public AnimationProperties setAnimationEndAction(Consumer<Property> listener) {
mAnimationEndAction = listener;
return this;
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 e47e4146d4c0..af608a7f3a64 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
@@ -5137,8 +5137,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
requestChildrenUpdate();
}
+ public void setShelf(NotificationShelf shelf) {
+ if (!NotificationShelfController.checkRefactorFlagEnabled(
+ mAmbientState.getFeatureFlags())) {
+ return;
+ }
+ int index = -1;
+ if (mShelf != null) {
+ index = indexOfChild(mShelf);
+ removeView(mShelf);
+ }
+ mShelf = shelf;
+ addView(mShelf, index);
+ mAmbientState.setShelf(mShelf);
+ mStateAnimator.setShelf(mShelf);
+ shelf.bind(mAmbientState, this, mController.getNotificationRoundnessManager());
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShelfController(NotificationShelfController notificationShelfController) {
+ NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 792746c60134..1c8727f1b092 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
@@ -73,6 +73,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
@@ -1382,6 +1383,7 @@ public class NotificationStackScrollLayoutController {
}
public void setShelfController(NotificationShelfController notificationShelfController) {
+ NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
mView.setShelfController(notificationShelfController);
}
@@ -1593,6 +1595,11 @@ public class NotificationStackScrollLayoutController {
mView.setOnNotificationRemovedListener(listener);
}
+ public void setShelf(NotificationShelf shelf) {
+ if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return;
+ mView.setShelf(shelf);
+ }
+
/**
* Enum for UiEvent logged from this class
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 092242814869..c7cb70c1da05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -23,12 +23,14 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.Compile
+import com.android.systemui.util.LargeScreenUtils.shouldUseSplitNotificationShade
import com.android.systemui.util.children
import java.io.PrintWriter
import javax.inject.Inject
@@ -51,11 +53,10 @@ class NotificationStackSizeCalculator
constructor(
private val statusBarStateController: SysuiStatusBarStateController,
private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
+ private val mediaDataManager: MediaDataManager,
@Main private val resources: Resources
) {
- private lateinit var lastComputeHeightLog : String
-
/**
* Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow shelf.
* If there are exactly 1 + mMaxKeyguardNotifications, and they fit in the available space
@@ -67,67 +68,157 @@ constructor(
/** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
private var dividerHeight by notNull<Float>()
+ /**
+ * True when there is not enough vertical space to show at least one notification with heads up
+ * layout. When true, notifications always show collapsed layout.
+ */
+ private var saveSpaceOnLockscreen = false
+
init {
updateResources()
}
/**
* Returns whether notifications and (shelf if visible) can fit in total space available.
- * [spaceForShelf] is extra vertical space allowed for the shelf to overlap the lock icon.
+ * [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon.
*/
private fun canStackFitInSpace(
stackHeight: StackHeight,
- spaceForNotifications: Float,
- spaceForShelf: Float,
- ): Boolean {
-
- val (notificationsHeight, shelfHeightWithSpaceBefore) = stackHeight
- var canFit: Boolean
+ notifSpace: Float,
+ shelfSpace: Float,
+ ): FitResult {
+ val (notifHeight, notifHeightSaveSpace, shelfHeightWithSpaceBefore) = stackHeight
if (shelfHeightWithSpaceBefore == 0f) {
- canFit = notificationsHeight <= spaceForNotifications
+ if (notifHeight <= notifSpace) {
+ log {
+ "\tcanStackFitInSpace[FIT] = notifHeight[$notifHeight]" +
+ " <= notifSpace[$notifSpace]"
+ }
+ return FitResult.FIT
+ }
+ if (notifHeightSaveSpace <= notifSpace) {
+ log {
+ "\tcanStackFitInSpace[FIT_IF_SAVE_SPACE]" +
+ " = notifHeightSaveSpace[$notifHeightSaveSpace]" +
+ " <= notifSpace[$notifSpace]"
+ }
+ return FitResult.FIT_IF_SAVE_SPACE
+ }
log {
- "canStackFitInSpace[$canFit] = notificationsHeight[$notificationsHeight]" +
- " <= spaceForNotifications[$spaceForNotifications]"
+ "\tcanStackFitInSpace[NO_FIT]" +
+ " = notifHeightSaveSpace[$notifHeightSaveSpace] > notifSpace[$notifSpace]"
}
+ return FitResult.NO_FIT
} else {
- canFit =
- (notificationsHeight + shelfHeightWithSpaceBefore) <=
- (spaceForNotifications + spaceForShelf)
- log {
- "canStackFitInSpace[$canFit] = (notificationsHeight[$notificationsHeight]" +
- " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
- " <= (spaceForNotifications[$spaceForNotifications] " +
- " + spaceForShelf[$spaceForShelf])"
+ if ((notifHeight + shelfHeightWithSpaceBefore) <= (notifSpace + shelfSpace)) {
+ log {
+ "\tcanStackFitInSpace[FIT] = (notifHeight[$notifHeight]" +
+ " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
+ " <= (notifSpace[$notifSpace] " +
+ " + spaceForShelf[$shelfSpace])"
+ }
+ return FitResult.FIT
+ } else if (
+ (notifHeightSaveSpace + shelfHeightWithSpaceBefore) <= (notifSpace + shelfSpace)
+ ) {
+ log {
+ "\tcanStackFitInSpace[FIT_IF_SAVE_SPACE]" +
+ " = (notifHeightSaveSpace[$notifHeightSaveSpace]" +
+ " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
+ " <= (notifSpace[$notifSpace] + shelfSpace[$shelfSpace])"
+ }
+ return FitResult.FIT_IF_SAVE_SPACE
+ } else {
+ log {
+ "\tcanStackFitInSpace[NO_FIT]" +
+ " = (notifHeightSaveSpace[$notifHeightSaveSpace]" +
+ " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
+ " > (notifSpace[$notifSpace] + shelfSpace[$shelfSpace])"
+ }
+ return FitResult.NO_FIT
}
}
- return canFit
}
/**
- * Given the [spaceForNotifications] and [spaceForShelf] constraints, calculate how many
- * notifications to show. This number is only valid in keyguard.
+ * Given the [notifSpace] and [shelfSpace] constraints, calculate how many notifications to
+ * show. This number is only valid in keyguard.
*
* @param totalAvailableSpace space for notifications. This includes the space for the shelf.
*/
fun computeMaxKeyguardNotifications(
stack: NotificationStackScrollLayout,
- spaceForNotifications: Float,
- spaceForShelf: Float,
- shelfIntrinsicHeight: Float
+ notifSpace: Float,
+ shelfSpace: Float,
+ shelfHeight: Float,
): Int {
+ log { "\n " }
+ log {
+ "computeMaxKeyguardNotifications ---" +
+ "\n\tnotifSpace $notifSpace" +
+ "\n\tspaceForShelf $shelfSpace" +
+ "\n\tshelfIntrinsicHeight $shelfHeight"
+ }
+ if (notifSpace + shelfSpace <= 0f) {
+ log { "--- No space to show anything. maxNotifs=0" }
+ return 0
+ }
log { "\n" }
- val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
- /* computeHeight= */ false)
+ val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight)
+ val isMediaShowing = mediaDataManager.hasActiveMediaOrRecommendation()
- var maxNotifications =
+ log { "\tGet maxNotifWithoutSavingSpace ---" }
+ val maxNotifWithoutSavingSpace =
stackHeightSequence.lastIndexWhile { heightResult ->
canStackFitInSpace(
heightResult,
- spaceForNotifications = spaceForNotifications,
- spaceForShelf = spaceForShelf)
+ notifSpace = notifSpace,
+ shelfSpace = shelfSpace
+ ) == FitResult.FIT
+ }
+
+ // How many notifications we can show at heightWithoutLockscreenConstraints
+ var minCountAtHeightWithoutConstraints =
+ if (isMediaShowing && !shouldUseSplitNotificationShade(resources)) 2 else 1
+ log {
+ "\t---maxNotifWithoutSavingSpace=$maxNotifWithoutSavingSpace " +
+ "isMediaShowing=$isMediaShowing" +
+ "minCountAtHeightWithoutConstraints=$minCountAtHeightWithoutConstraints"
+ }
+ log { "\n" }
+
+ var maxNotifications: Int
+ if (maxNotifWithoutSavingSpace >= minCountAtHeightWithoutConstraints) {
+ saveSpaceOnLockscreen = false
+ maxNotifications = maxNotifWithoutSavingSpace
+ log {
+ "\tDo NOT save space. maxNotifications=maxNotifWithoutSavingSpace=$maxNotifications"
+ }
+ } else {
+ log { "\tSAVE space ---" }
+ saveSpaceOnLockscreen = true
+ maxNotifications =
+ stackHeightSequence.lastIndexWhile { heightResult ->
+ canStackFitInSpace(
+ heightResult,
+ notifSpace = notifSpace,
+ shelfSpace = shelfSpace
+ ) != FitResult.NO_FIT
+ }
+ log { "\t--- maxNotifications=$maxNotifications" }
+ }
+
+ // Must update views immediately to avoid mismatches between initial HUN layout height
+ // and the height adapted to lockscreen space constraints, which causes jump cuts.
+ stack.showableChildren().toList().forEach { currentNotification ->
+ run {
+ if (currentNotification is ExpandableNotificationRow) {
+ currentNotification.saveSpaceOnLockscreen = saveSpaceOnLockscreen
+ }
}
+ }
if (onLockscreen()) {
maxNotifications = min(maxKeyguardNotifications, maxNotifications)
@@ -137,53 +228,80 @@ constructor(
maxNotifications = max(0, maxNotifications)
log {
val sequence = if (SPEW) " stackHeightSequence=${stackHeightSequence.toList()}" else ""
- "computeMaxKeyguardNotifications(" +
- " spaceForNotifications=$spaceForNotifications" +
- " spaceForShelf=$spaceForShelf" +
- " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications$sequence"
+ "--- computeMaxKeyguardNotifications(" +
+ " notifSpace=$notifSpace" +
+ " shelfSpace=$shelfSpace" +
+ " shelfHeight=$shelfHeight) -> $maxNotifications$sequence"
}
+ log { "\n" }
return maxNotifications
}
/**
- * Given the [maxNotifications] constraint, calculates the height of the
+ * Given the [maxNotifs] constraint, calculates the height of the
* [NotificationStackScrollLayout]. This might or might not be in keyguard.
*
* @param stack stack containing notifications as children.
- * @param maxNotifications Maximum number of notifications. When reached, the others will go
- * into the shelf.
- * @param shelfIntrinsicHeight height of the shelf, without any padding. It might be zero.
- *
+ * @param maxNotifs Maximum number of notifications. When reached, the others will go into the
+ * shelf.
+ * @param shelfHeight height of the shelf, without any padding. It might be zero.
* @return height of the stack, including shelf height, if needed.
*/
fun computeHeight(
stack: NotificationStackScrollLayout,
- maxNotifications: Int,
- shelfIntrinsicHeight: Float
+ maxNotifs: Int,
+ shelfHeight: Float
): Float {
log { "\n" }
- lastComputeHeightLog = ""
- val heightPerMaxNotifications =
- computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
- /* computeHeight= */ true)
-
- val (notificationsHeight, shelfHeightWithSpaceBefore) =
- heightPerMaxNotifications.elementAtOrElse(maxNotifications) {
- heightPerMaxNotifications.last() // Height with all notifications visible.
+ log { "computeHeight ---" }
+
+ val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight)
+
+ val (notifsHeight, notifsHeightSavingSpace, shelfHeightWithSpaceBefore) =
+ stackHeightSequence.elementAtOrElse(maxNotifs) {
+ stackHeightSequence.last() // Height with all notifications visible.
+ }
+
+ var height: Float
+ if (saveSpaceOnLockscreen) {
+ height = notifsHeightSavingSpace + shelfHeightWithSpaceBefore
+ log {
+ "--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" +
+ " -> $height=($notifsHeightSavingSpace+$shelfHeightWithSpaceBefore)," +
+ " | saveSpaceOnLockscreen=$saveSpaceOnLockscreen"
+ }
+ } else {
+ height = notifsHeight + shelfHeightWithSpaceBefore
+ log {
+ "--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" +
+ " -> ${height}=($notifsHeight+$shelfHeightWithSpaceBefore)" +
+ " | saveSpaceOnLockscreen=$saveSpaceOnLockscreen"
}
- lastComputeHeightLog += "\ncomputeHeight(maxNotifications=$maxNotifications," +
- "shelfIntrinsicHeight=$shelfIntrinsicHeight) -> " +
- "${notificationsHeight + shelfHeightWithSpaceBefore}" +
- " = ($notificationsHeight + $shelfHeightWithSpaceBefore)"
- log {
- lastComputeHeightLog
}
- return notificationsHeight + shelfHeightWithSpaceBefore
+ return height
}
+ private enum class FitResult {
+ FIT,
+ FIT_IF_SAVE_SPACE,
+ NO_FIT
+ }
+
+ data class SpaceNeeded(
+ // Float height of spaceNeeded when showing heads up layout for FSI HUNs.
+ val whenEnoughSpace: Float,
+
+ // Float height of space needed when showing collapsed layout for FSI HUNs.
+ val whenSavingSpace: Float
+ )
+
private data class StackHeight(
// Float height with ith max notifications (not including shelf)
- val notificationsHeight: Float,
+ val notifsHeight: Float,
+
+ // Float height with ith max notifications
+ // (not including shelf, using collapsed layout for FSI HUN)
+ val notifsHeightSavingSpace: Float,
// Float height of shelf (0 if shelf is not showing), and space before the shelf that
// changes during the lockscreen <=> full shade transition.
@@ -193,20 +311,27 @@ constructor(
private fun computeHeightPerNotificationLimit(
stack: NotificationStackScrollLayout,
shelfHeight: Float,
- computeHeight: Boolean
): Sequence<StackHeight> = sequence {
- log { "computeHeightPerNotificationLimit" }
-
val children = stack.showableChildren().toList()
var notifications = 0f
+ var notifsWithCollapsedHun = 0f
var previous: ExpandableView? = null
val onLockscreen = onLockscreen()
// Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
- yield(StackHeight(notificationsHeight = 0f, shelfHeightWithSpaceBefore = shelfHeight))
+ yield(
+ StackHeight(
+ notifsHeight = 0f,
+ notifsHeightSavingSpace = 0f,
+ shelfHeightWithSpaceBefore = shelfHeight
+ )
+ )
children.forEachIndexed { i, currentNotification ->
- notifications += spaceNeeded(currentNotification, i, previous, stack, onLockscreen)
+ val space = getSpaceNeeded(currentNotification, i, previous, stack, onLockscreen)
+ notifications += space.whenEnoughSpace
+ notifsWithCollapsedHun += space.whenSavingSpace
+
previous = currentNotification
val shelfWithSpaceBefore =
@@ -219,22 +344,23 @@ constructor(
stack,
previous = currentNotification,
current = children[firstViewInShelfIndex],
- currentIndex = firstViewInShelfIndex)
+ currentIndex = firstViewInShelfIndex
+ )
spaceBeforeShelf + shelfHeight
}
- val currentLog = "computeHeight | i=$i notificationsHeight=$notifications " +
- "shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
- if (computeHeight) {
- lastComputeHeightLog += "\n" + currentLog
- }
log {
- currentLog
+ "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
+ "notifsHeightSavingSpace=$notifsWithCollapsedHun" +
+ " shelfWithSpaceBefore=$shelfWithSpaceBefore"
}
yield(
StackHeight(
- notificationsHeight = notifications,
- shelfHeightWithSpaceBefore = shelfWithSpaceBefore))
+ notifsHeight = notifications,
+ notifsHeightSavingSpace = notifsWithCollapsedHun,
+ shelfHeightWithSpaceBefore = shelfWithSpaceBefore
+ )
+ )
}
}
@@ -256,32 +382,46 @@ constructor(
}
@VisibleForTesting
- fun spaceNeeded(
+ fun getSpaceNeeded(
view: ExpandableView,
visibleIndex: Int,
previousView: ExpandableView?,
stack: NotificationStackScrollLayout,
- onLockscreen: Boolean
- ): Float {
+ onLockscreen: Boolean,
+ ): SpaceNeeded {
assert(view.isShowable(onLockscreen))
+ // Must use heightWithoutLockscreenConstraints because intrinsicHeight references
+ // mSaveSpaceOnLockscreen and using intrinsicHeight here will result in stack overflow.
+ val height = view.heightWithoutLockscreenConstraints.toFloat()
+ val gapAndDividerHeight =
+ calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
+
var size =
if (onLockscreen) {
if (view is ExpandableNotificationRow && view.entry.isStickyAndNotDemoted) {
- view.intrinsicHeight.toFloat()
+ height
} else {
view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
}
} else {
- view.intrinsicHeight.toFloat()
+ height
+ }
+ size += gapAndDividerHeight
+
+ var sizeWhenSavingSpace =
+ if (onLockscreen) {
+ view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
+ } else {
+ height
}
+ sizeWhenSavingSpace += gapAndDividerHeight
- size += calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
- return size
+ return SpaceNeeded(size, sizeWhenSavingSpace)
}
fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("NotificationStackSizeCalculator lastComputeHeightLog = $lastComputeHeightLog")
+ pw.println("NotificationStackSizeCalculator saveSpaceOnLockscreen=$saveSpaceOnLockscreen")
}
private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 8ee2c6f2c399..74ab47ff27a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -29,6 +29,7 @@ import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.Assert
import com.android.systemui.util.sensors.AsyncSensorManager
@@ -46,6 +47,7 @@ class KeyguardLiftController @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val asyncSensorManager: AsyncSensorManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
private val dumpManager: DumpManager
) : Dumpable, CoreStartable {
@@ -72,6 +74,7 @@ class KeyguardLiftController @Inject constructor(
// Not listening anymore since trigger events unregister themselves
isListening = false
updateListeningState()
+ keyguardFaceAuthInteractor.onDeviceLifted()
keyguardUpdateMonitor.requestFaceAuth(
FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 118bfc55dd4c..0cd3401ae541 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -175,15 +175,19 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// We animate the Y properly separately using the PropertyAnimator, as the panel
// view also needs to update the end position.
PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y)
- PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY,
+ PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY,
AnimationProperties().setDuration(duration.toLong()),
true /* animate */)
- keyguardView.animate()
+ // Cancel any existing CUJs before starting the animation
+ interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+
+ PropertyAnimator.setProperty(
+ keyguardView, AnimatableProperty.ALPHA, 1f,
+ AnimationProperties()
+ .setDelay(0)
.setDuration(duration.toLong())
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .alpha(1f)
- .withEndAction {
+ .setAnimationEndAction {
aodUiAnimationPlaying = false
// Lock the keyguard if it was waiting for the screen off animation to end.
@@ -199,30 +203,23 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// Done going to sleep, reset this flag.
decidedToAnimateGoingToSleep = null
- // We need to unset the listener. These are persistent for future animators
- keyguardView.animate().setListener(null)
interactionJankMonitor.end(CUJ_SCREEN_OFF_SHOW_AOD)
}
- .setListener(object : AnimatorListenerAdapter() {
- override fun onAnimationCancel(animation: Animator?) {
- // If we're cancelled, reset state flags/listeners. The end action above
- // will not be called, which is what we want since that will finish the
- // screen off animation and show the lockscreen, which we don't want if we
- // were cancelled.
- aodUiAnimationPlaying = false
- decidedToAnimateGoingToSleep = null
- keyguardView.animate().setListener(null)
-
- interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
- }
-
- override fun onAnimationStart(animation: Animator?) {
- interactionJankMonitor.begin(
- mCentralSurfaces.notificationShadeWindowView,
- CUJ_SCREEN_OFF_SHOW_AOD)
- }
- })
- .start()
+ .setAnimationCancelAction {
+ // If we're cancelled, reset state flags/listeners. The end action above
+ // will not be called, which is what we want since that will finish the
+ // screen off animation and show the lockscreen, which we don't want if we
+ // were cancelled.
+ aodUiAnimationPlaying = false
+ decidedToAnimateGoingToSleep = null
+ interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
+ }
+ .setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_SLOW_IN),
+ true /* animate */)
+ interactionJankMonitor.begin(
+ mCentralSurfaces.notificationShadeWindowView,
+ CUJ_SCREEN_OFF_SHOW_AOD
+ )
}
override fun onStartedWakingUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index 075e6ec11ae7..a05ab849088d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -26,15 +26,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsVi
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/**
@@ -46,39 +38,16 @@ import kotlinx.coroutines.launch
* the list of available mobile lines of service for which we want to show icons.
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class MobileUiAdapter
@Inject
constructor(
- interactor: MobileIconsInteractor,
private val iconController: StatusBarIconController,
- private val iconsViewModelFactory: MobileIconsViewModel.Factory,
+ val mobileIconsViewModel: MobileIconsViewModel,
private val logger: MobileViewLogger,
@Application private val scope: CoroutineScope,
private val statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable {
- private val mobileSubIds: Flow<List<Int>> =
- interactor.filteredSubscriptions.mapLatest { subscriptions ->
- subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
- }
-
- /**
- * We expose the list of tracked subscriptions as a flow of a list of ints, where each int is
- * the subscriptionId of the relevant subscriptions. These act as a key into the layouts which
- * house the mobile infos.
- *
- * NOTE: this should go away as the view presenter learns more about this data pipeline
- */
- private val mobileSubIdsState: StateFlow<List<Int>> =
- mobileSubIds
- .distinctUntilChanged()
- .onEach { logger.logUiAdapterSubIdsUpdated(it) }
- .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
-
- /** In order to keep the logs tame, we will reuse the same top-level mobile icons view model */
- val mobileIconsViewModel = iconsViewModelFactory.create(mobileSubIdsState)
-
private var isCollecting: Boolean = false
private var lastValue: List<Int>? = null
@@ -90,7 +59,7 @@ constructor(
if (statusBarPipelineFlags.useNewMobileIcons()) {
scope.launch {
isCollecting = true
- mobileSubIds.collectLatest {
+ mobileIconsViewModel.subscriptionIdsFlow.collectLatest {
logger.logUiAdapterSubIdsSentToIconController(it)
lastValue = it
iconController.setNewMobileIconSubIds(it)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
index 90dff23c637c..f2f91430eba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -41,15 +41,6 @@ constructor(
private val collectionStatuses = mutableMapOf<String, Boolean>()
- fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
- buffer.log(
- TAG,
- LogLevel.INFO,
- { str1 = subs.toString() },
- { "Sub IDs in MobileUiAdapter updated internally: $str1" },
- )
- }
-
fun logUiAdapterSubIdsSentToIconController(subs: List<Int>) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 2b90065284d0..9af5e836659f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -29,18 +29,23 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMob
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/**
* View model for describing the system's current mobile cellular connections. The result is a list
* of [MobileIconViewModel]s which describe the individual icons and can be bound to
- * [ModernStatusBarMobileView]
+ * [ModernStatusBarMobileView].
*/
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
class MobileIconsViewModel
@Inject
constructor(
- val subscriptionIdsFlow: StateFlow<List<Int>>,
val logger: MobileViewLogger,
private val verboseLogger: VerboseMobileViewLogger,
private val interactor: MobileIconsInteractor,
@@ -51,6 +56,13 @@ constructor(
) {
@VisibleForTesting val mobileIconSubIdCache = mutableMapOf<Int, MobileIconViewModel>()
+ val subscriptionIdsFlow: StateFlow<List<Int>> =
+ interactor.filteredSubscriptions
+ .mapLatest { subscriptions ->
+ subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+
init {
scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } }
}
@@ -79,30 +91,4 @@ constructor(
val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
}
-
- @SysUISingleton
- class Factory
- @Inject
- constructor(
- private val logger: MobileViewLogger,
- private val verboseLogger: VerboseMobileViewLogger,
- private val interactor: MobileIconsInteractor,
- private val airplaneModeInteractor: AirplaneModeInteractor,
- private val constants: ConnectivityConstants,
- @Application private val scope: CoroutineScope,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
- ) {
- fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
- return MobileIconsViewModel(
- subscriptionIdsFlow,
- logger,
- verboseLogger,
- interactor,
- airplaneModeInteractor,
- constants,
- scope,
- statusBarPipelineFlags,
- )
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index e9f0dcb4eb51..928e0115287d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -61,6 +61,7 @@ import javax.inject.Inject;
* Manages the user switcher on the Keyguard.
*/
@KeyguardUserSwitcherScope
+@Deprecated
public class KeyguardUserSwitcherController extends ViewController<KeyguardUserSwitcherView> {
private static final String TAG = "KeyguardUserSwitcherController";
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index a20a5b2fdbbc..e819f946a6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -31,6 +31,7 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
+import androidx.annotation.DimenRes
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
import com.android.internal.widget.CachingIconView
@@ -180,8 +181,9 @@ constructor(
// Button
val buttonView = currentView.requireViewById<TextView>(R.id.end_button)
- if (newInfo.endItem is ChipbarEndItem.Button) {
- TextViewBinder.bind(buttonView, newInfo.endItem.text)
+ val hasButton = newInfo.endItem is ChipbarEndItem.Button
+ if (hasButton) {
+ TextViewBinder.bind(buttonView, (newInfo.endItem as ChipbarEndItem.Button).text)
val onClickListener =
View.OnClickListener { clickedView ->
@@ -196,6 +198,12 @@ constructor(
buttonView.visibility = View.GONE
}
+ currentView
+ .getInnerView()
+ .setEndPadding(
+ if (hasButton) R.dimen.chipbar_outer_padding_half else R.dimen.chipbar_outer_padding
+ )
+
// ---- Overall accessibility ----
val iconDesc = newInfo.startIcon.icon.contentDescription
val loadedIconDesc =
@@ -309,6 +317,15 @@ constructor(
viewUtil.setRectToViewWindowLocation(view, outRect)
}
+ private fun View.setEndPadding(@DimenRes endPaddingDimen: Int) {
+ this.setPaddingRelative(
+ this.paddingStart,
+ this.paddingTop,
+ context.resources.getDimensionPixelSize(endPaddingDimen),
+ this.paddingBottom,
+ )
+ }
+
private fun Boolean.visibleIfTrue(): Int {
return if (this) {
View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 6e58f2296c86..52f2d11f814e 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay.chipbar
import android.os.VibrationEffect
import android.view.View
-import androidx.annotation.ColorRes
+import androidx.annotation.AttrRes
import com.android.systemui.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
@@ -49,7 +49,9 @@ data class ChipbarInfo(
override val priority: ViewPriority,
) : TemporaryViewInfo() {
companion object {
- @ColorRes val DEFAULT_ICON_TINT = R.color.chipbar_text_and_icon_color
+ // LINT.IfChange
+ @AttrRes val DEFAULT_ICON_TINT = com.android.internal.R.attr.materialColorOnSecondaryFixed
+ // LINT.ThenChange(systemui/res/layout/chipbar.xml)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 05e566690f57..29f16c7b924a 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -272,10 +272,10 @@ public class SystemUIToast implements ToastPlugin.Toast {
private static boolean showApplicationIcon(ApplicationInfo appInfo,
PackageManager packageManager) {
- if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) {
+ if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP | FLAG_SYSTEM)) {
return packageManager.getLaunchIntentForPackage(appInfo.packageName) != null;
}
- return !hasFlag(appInfo.flags, FLAG_SYSTEM);
+ return true;
}
private static boolean hasFlag(int flags, int flag) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt
new file mode 100644
index 000000000000..c587f2edd601
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import dagger.Lazy
+import kotlin.reflect.KProperty
+
+/**
+ * Extension operator that allows developers to use [dagger.Lazy] as a property delegate:
+ * ```kotlin
+ * class MyClass @Inject constructor(
+ * lazyDependency: dagger.Lazy<Foo>,
+ * ) {
+ * val dependency: Foo by lazyDependency
+ * }
+ * ```
+ */
+operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = get()
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 209ea41fed61..58cffa761a66 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -58,6 +58,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -418,6 +419,7 @@ public class GarbageMonitor implements Dumpable {
@Inject
public MemoryTile(
QSHost host,
+ QsEventLogger uiEventLogger,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
FalsingManager falsingManager,
@@ -428,7 +430,7 @@ public class GarbageMonitor implements Dumpable {
GarbageMonitor monitor,
PanelInteractor panelInteractor
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
gm = monitor;
mPanelInteractor = panelInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
index 35af44442ca9..e3ed2b405fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
@@ -16,20 +16,34 @@
package com.android.systemui.volume;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+
import android.annotation.StringRes;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
-import android.os.CountDownTimer;
+import android.provider.Settings;
import android.util.Log;
import android.view.KeyEvent;
import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.NotificationChannels;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
/**
* A class that implements the four Computed Sound Dose-related warnings defined in {@link AudioManager}:
@@ -53,34 +67,58 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
* communication between the native audio framework that implements the dose computation and the
* audio service.
*/
-public abstract class CsdWarningDialog extends SystemUIDialog
+public class CsdWarningDialog extends SystemUIDialog
implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
private static final String TAG = Util.logTag(CsdWarningDialog.class);
private static final int KEY_CONFIRM_ALLOWED_AFTER_MS = 1000; // milliseconds
// time after which action is taken when the user hasn't ack'd or dismissed the dialog
- private static final int NO_ACTION_TIMEOUT_MS = 5000;
+ public static final int NO_ACTION_TIMEOUT_MS = 5000;
private final Context mContext;
private final AudioManager mAudioManager;
private final @AudioManager.CsdWarning int mCsdWarning;
private final Object mTimerLock = new Object();
+
/**
* Timer to keep track of how long the user has before an action (here volume reduction) is
* taken on their behalf.
*/
@GuardedBy("mTimerLock")
- private final CountDownTimer mNoUserActionTimer;
+ private Runnable mNoUserActionRunnable;
+ private Runnable mCancelScheduledNoUserActionRunnable = null;
+
+ private final DelayableExecutor mDelayableExecutor;
+ private NotificationManager mNotificationManager;
+ private Runnable mOnCleanup;
private long mShowTime;
- public CsdWarningDialog(@AudioManager.CsdWarning int csdWarning, Context context,
- AudioManager audioManager) {
+ /**
+ * To inject dependencies and allow for easier testing
+ */
+ @AssistedFactory
+ public interface Factory {
+ /**
+ * Create a dialog object
+ */
+ CsdWarningDialog create(int csdWarning, Runnable onCleanup);
+ }
+
+ @AssistedInject
+ public CsdWarningDialog(@Assisted @AudioManager.CsdWarning int csdWarning, Context context,
+ AudioManager audioManager, NotificationManager notificationManager,
+ @Background DelayableExecutor delayableExecutor, @Assisted Runnable onCleanup) {
super(context);
mCsdWarning = csdWarning;
mContext = context;
mAudioManager = audioManager;
+ mNotificationManager = notificationManager;
+ mOnCleanup = onCleanup;
+
+ mDelayableExecutor = delayableExecutor;
+
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
setShowForAllUsers(true);
setMessage(mContext.getString(getStringForWarning(csdWarning)));
@@ -95,25 +133,24 @@ public abstract class CsdWarningDialog extends SystemUIDialog
Context.RECEIVER_EXPORTED_UNAUDITED);
if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
- mNoUserActionTimer = new CountDownTimer(NO_ACTION_TIMEOUT_MS, NO_ACTION_TIMEOUT_MS) {
- @Override
- public void onTick(long millisUntilFinished) { }
-
- @Override
- public void onFinish() {
- if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
- // unlike on the 5x dose repeat, level is only reduced to RS1
- // when the warning is not acknowledged quick enough
- mAudioManager.lowerVolumeToRs1();
- }
+ mNoUserActionRunnable = () -> {
+ if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
+ // unlike on the 5x dose repeat, level is only reduced to RS1 when the warning
+ // is not acknowledged quickly enough
+ mAudioManager.lowerVolumeToRs1();
+ sendNotification();
}
};
} else {
- mNoUserActionTimer = null;
+ mNoUserActionRunnable = null;
}
}
- protected abstract void cleanUp();
+ private void cleanUp() {
+ if (mOnCleanup != null) {
+ mOnCleanup.run();
+ }
+ }
// NOT overriding onKeyDown as we're not allowing a dismissal on any key other than
// VOLUME_DOWN, and for this, we don't need to track if it's the start of a new
@@ -153,12 +190,9 @@ public abstract class CsdWarningDialog extends SystemUIDialog
super.onStart();
mShowTime = System.currentTimeMillis();
synchronized (mTimerLock) {
- if (mNoUserActionTimer != null) {
- new Thread(() -> {
- synchronized (mTimerLock) {
- mNoUserActionTimer.start();
- }
- }).start();
+ if (mNoUserActionRunnable != null) {
+ mCancelScheduledNoUserActionRunnable = mDelayableExecutor.executeDelayed(
+ mNoUserActionRunnable, NO_ACTION_TIMEOUT_MS);
}
}
}
@@ -166,8 +200,8 @@ public abstract class CsdWarningDialog extends SystemUIDialog
@Override
protected void onStop() {
synchronized (mTimerLock) {
- if (mNoUserActionTimer != null) {
- mNoUserActionTimer.cancel();
+ if (mCancelScheduledNoUserActionRunnable != null) {
+ mCancelScheduledNoUserActionRunnable.run();
}
}
}
@@ -212,4 +246,32 @@ public abstract class CsdWarningDialog extends SystemUIDialog
Log.e(TAG, "Invalid CSD warning event " + csdWarning, new Exception());
return com.android.internal.R.string.csd_dose_reached_warning;
}
+
+
+ /**
+ * In case user did not respond to the dialog, they still need to know volume was lowered.
+ */
+ private void sendNotification() {
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent,
+ FLAG_IMMUTABLE);
+
+ String text = mContext.getString(R.string.csd_system_lowered_text);
+ String title = mContext.getString(R.string.csd_lowered_title);
+
+ Notification.Builder builder =
+ new Notification.Builder(mContext, NotificationChannels.ALERTS)
+ .setSmallIcon(R.drawable.hearing)
+ .setContentTitle(title)
+ .setContentText(text)
+ .setContentIntent(pendingIntent)
+ .setStyle(new Notification.BigTextStyle().bigText(text))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setLocalOnly(true)
+ .setAutoCancel(true)
+ .setCategory(Notification.CATEGORY_SYSTEM);
+
+ mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO,
+ builder.build());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 95cc12a48bb2..3c007f99a654 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -263,6 +263,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private final ConfigurationController mConfigurationController;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
private final VolumePanelFactory mVolumePanelFactory;
+ private final CsdWarningDialog.Factory mCsdWarningDialogFactory;
private final ActivityStarter mActivityStarter;
private boolean mShowing;
@@ -311,6 +312,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
InteractionJankMonitor interactionJankMonitor,
DeviceConfigProxy deviceConfigProxy,
Executor executor,
+ CsdWarningDialog.Factory csdWarningDialogFactory,
DumpManager dumpManager) {
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
@@ -322,6 +324,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mConfigurationController = configurationController;
mMediaOutputDialogFactory = mediaOutputDialogFactory;
mVolumePanelFactory = volumePanelFactory;
+ mCsdWarningDialogFactory = csdWarningDialogFactory;
mActivityStarter = activityStarter;
mShowActiveStreamOnly = showActiveStreamOnly();
mHasSeenODICaptionsTooltip =
@@ -2084,21 +2087,21 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
rescheduleTimeoutH();
}
- private void showCsdWarningH(int csdWarning, int durationMs) {
+ @VisibleForTesting void showCsdWarningH(int csdWarning, int durationMs) {
synchronized (mSafetyWarningLock) {
+
if (mCsdDialog != null) {
return;
}
- mCsdDialog = new CsdWarningDialog(csdWarning,
- mContext, mController.getAudioManager()) {
- @Override
- protected void cleanUp() {
- synchronized (mSafetyWarningLock) {
- mCsdDialog = null;
- }
- recheckH(null);
+
+ final Runnable cleanUp = () -> {
+ synchronized (mSafetyWarningLock) {
+ mCsdDialog = null;
}
+ recheckH(null);
};
+
+ mCsdDialog = mCsdWarningDialogFactory.create(csdWarning, cleanUp);
mCsdDialog.show();
}
recheckH(null);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 0ab6c690e1e1..14d3ca334073 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -30,17 +30,18 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.volume.CsdWarningDialog;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumePanelFactory;
-import java.util.concurrent.Executor;
-
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import java.util.concurrent.Executor;
+
/** Dagger Module for code in the volume package. */
@Module
@@ -63,6 +64,7 @@ public interface VolumeModule {
InteractionJankMonitor interactionJankMonitor,
DeviceConfigProxy deviceConfigProxy,
@Main Executor executor,
+ CsdWarningDialog.Factory csdFactory,
DumpManager dumpManager) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
@@ -76,6 +78,7 @@ public interface VolumeModule {
interactionJankMonitor,
deviceConfigProxy,
executor,
+ csdFactory,
dumpManager);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index d760189bcfd6..3e4fd891a668 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -67,6 +67,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -208,7 +209,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mConfigurationController, mFalsingCollector, mFalsingManager,
mUserSwitcherController, mFeatureFlags, mGlobalSettings,
mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate,
- mTelephonyManager, mViewMediatorCallback, mAudioManager);
+ mTelephonyManager, mViewMediatorCallback, mAudioManager,
+ mock(KeyguardFaceAuthInteractor.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index f1ee1080f6c0..2c1d2adb11ee 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -16,7 +16,9 @@
package com.android.keyguard;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -25,6 +27,9 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.ClockConfig;
+import com.android.systemui.plugins.ClockController;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -76,7 +81,16 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mScreenOffAnimationController,
mKeyguardLogger,
mFeatureFlags,
- mInteractionJankMonitor);
+ mInteractionJankMonitor) {
+ @Override
+ void setProperty(
+ AnimatableProperty property,
+ float value,
+ boolean animate) {
+ // Route into the mock version for verification
+ mControllerMock.setProperty(property, value, animate);
+ }
+ };
}
@Test
@@ -111,4 +125,34 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
configurationListenerArgumentCaptor.getValue().onLocaleListChanged();
verify(mKeyguardClockSwitchController).onLocaleListChanged();
}
+
+ @Test
+ public void updatePosition_primaryClockAnimation() {
+ ClockController mockClock = mock(ClockController.class);
+ when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
+ when(mockClock.getConfig()).thenReturn(new ClockConfig(false, false, true));
+
+ mController.updatePosition(10, 15, 20f, true);
+
+ verify(mControllerMock).setProperty(AnimatableProperty.Y, 15f, true);
+ verify(mKeyguardClockSwitchController).updatePosition(
+ 10, 20f, KeyguardStatusViewController.CLOCK_ANIMATION_PROPERTIES, true);
+ verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 1f, true);
+ verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 1f, true);
+ }
+
+ @Test
+ public void updatePosition_alternateClockAnimation() {
+ ClockController mockClock = mock(ClockController.class);
+ when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
+ when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true, true));
+
+ mController.updatePosition(10, 15, 20f, true);
+
+ verify(mControllerMock).setProperty(AnimatableProperty.Y, 15f, true);
+ verify(mKeyguardClockSwitchController).updatePosition(
+ 10, 1f, KeyguardStatusViewController.CLOCK_ANIMATION_PROPERTIES, true);
+ verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 20f, true);
+ verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 20f, true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 0978c824cb15..9c36af3a35e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.accessibility;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.view.Choreographer.FrameCallback;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
@@ -172,6 +173,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
returnsSecondArg());
mResources = getContext().getOrCreateTestableResources().getResources();
+ // prevent the config orientation from undefined, which may cause config.diff method
+ // neglecting the orientation update.
+ if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) {
+ mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
+ }
+
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
mContext, mValueAnimator);
mWindowMagnificationController =
@@ -688,7 +695,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
- final Configuration config = mContext.getResources().getConfiguration();
+ // the config orientation should not be undefined, since it would cause config.diff
+ // returning 0 and thus the orientation changed would not be detected
+ assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation);
+
+ final Configuration config = mResources.getConfiguration();
config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
: ORIENTATION_LANDSCAPE;
final int newRotation = simulateRotateTheDevice();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
index 921f9a8fc7d6..2b95973363ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
@@ -37,7 +37,13 @@ class OnBackAnimationCallbackExtensionTest : SysuiTestCase() {
@Test
fun onBackProgressed_shouldInvoke_onBackProgress() {
- val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT)
+ val backEvent =
+ BackEvent(
+ /* touchX = */ 0f,
+ /* touchY = */ 0f,
+ /* progress = */ 0f,
+ /* swipeEdge = */ BackEvent.EDGE_LEFT
+ )
onBackAnimationCallback.onBackStarted(backEvent)
onBackAnimationCallback.onBackProgressed(backEvent)
@@ -47,7 +53,13 @@ class OnBackAnimationCallbackExtensionTest : SysuiTestCase() {
@Test
fun onBackStarted_shouldInvoke_onBackStart() {
- val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT)
+ val backEvent =
+ BackEvent(
+ /* touchX = */ 0f,
+ /* touchY = */ 0f,
+ /* progress = */ 0f,
+ /* swipeEdge = */ BackEvent.EDGE_LEFT
+ )
onBackAnimationCallback.onBackStarted(backEvent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 64c028e6a095..eae95a54744b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -87,6 +87,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
@@ -311,7 +312,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor,
mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker,
- mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils);
+ mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils,
+ mock(KeyguardFaceAuthInteractor.class));
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 08427dab978b..21397d97b578 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -269,6 +269,30 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
}
@Test
+ public void testInputEventPropagationAfterRemoval() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ // Ensure session started
+ final DreamTouchHandler.TouchSession session = captureSession(touchHandler);
+ final InputChannelCompat.InputEventListener eventListener =
+ registerInputEventListener(session);
+
+ session.pop();
+ environment.executeAll();
+
+ final InputEvent event = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(event);
+
+ verify(eventListener, never()).onInputEvent(eq(event));
+ }
+
+ @Test
public void testInputGesturePropagation() {
final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 2489e043c7db..f21ea3dbed6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -54,6 +54,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.log.SessionTracker
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -61,8 +62,11 @@ import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.captureMany
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -88,8 +92,6 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.StringWriter
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -183,8 +185,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
private fun createDeviceEntryFaceAuthRepositoryImpl(
fmOverride: FaceManager? = faceManager,
bypassControllerOverride: KeyguardBypassController? = bypassController
- ) =
- DeviceEntryFaceAuthRepositoryImpl(
+ ): DeviceEntryFaceAuthRepositoryImpl {
+ val systemClock = FakeSystemClock()
+ val faceAuthBuffer = TableLogBuffer(10, "face auth", systemClock)
+ val faceDetectBuffer = TableLogBuffer(10, "face detect", systemClock)
+ return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
fakeUserRepository,
@@ -200,8 +205,11 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
keyguardRepository,
keyguardInteractor,
alternateBouncerInteractor,
+ faceDetectBuffer,
+ faceAuthBuffer,
dumpManager,
)
+ }
@Test
fun faceAuthRunsAndProvidesAuthStatusUpdates() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
new file mode 100644
index 000000000000..3d1d2f46a65e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Handler
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: SystemUIKeyguardFaceAuthInteractor
+ private lateinit var testScope: TestScope
+ private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+ private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private lateinit var faceAuthRepository: FakeDeviceEntryFaceAuthRepository
+
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ val scheduler = TestCoroutineScheduler()
+ val dispatcher = StandardTestDispatcher(scheduler)
+ testScope = TestScope(dispatcher)
+ val featureFlags = FakeFeatureFlags()
+ featureFlags.set(Flags.FACE_AUTH_REFACTOR, true)
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
+ keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ keyguardTransitionInteractor = KeyguardTransitionInteractor(keyguardTransitionRepository)
+
+ underTest =
+ SystemUIKeyguardFaceAuthInteractor(
+ testScope.backgroundScope,
+ dispatcher,
+ faceAuthRepository,
+ PrimaryBouncerInteractor(
+ bouncerRepository,
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mock(KeyguardStateController::class.java),
+ mock(KeyguardSecurityModel::class.java),
+ mock(PrimaryBouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ context,
+ keyguardUpdateMonitor,
+ mock(KeyguardBypassController::class.java),
+ ),
+ AlternateBouncerInteractor(
+ mock(StatusBarStateController::class.java),
+ mock(KeyguardStateController::class.java),
+ bouncerRepository,
+ mock(BiometricSettingsRepository::class.java),
+ FakeDeviceEntryFingerprintAuthRepository(),
+ FakeSystemClock(),
+ ),
+ keyguardTransitionInteractor,
+ featureFlags,
+ FaceAuthenticationLogger(logcatLogBuffer("faceAuthBuffer")),
+ keyguardUpdateMonitor,
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromOffState() =
+ testScope.runTest {
+ underTest.start()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ KeyguardState.OFF,
+ KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED
+ )
+ )
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, true)
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromAodState() =
+ testScope.runTest {
+ underTest.start()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ KeyguardState.AOD,
+ KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED
+ )
+ )
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, true)
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenLockscreenBecomesVisibleFromDozingState() =
+ testScope.runTest {
+ underTest.start()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ KeyguardState.DOZING,
+ KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED
+ )
+ )
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED, true)
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenPrimaryBouncerIsVisible() =
+ testScope.runTest {
+ underTest.start()
+
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ bouncerRepository.setPrimaryShow(true)
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, true))
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenAlternateBouncerIsVisible() =
+ testScope.runTest {
+ underTest.start()
+
+ bouncerRepository.setAlternateVisible(false)
+ runCurrent()
+
+ bouncerRepository.setAlternateVisible(true)
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
+ false
+ )
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenUdfpsSensorTouched() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onUdfpsSensorTouched()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN, false))
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenOnAssistantTriggeredOnLockScreen() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onAssistantTriggeredOnLockScreen()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED, true)
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenDeviceLifted() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onDeviceLifted()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED, true)
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenQsExpansionStared() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onQsExpansionStared()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true))
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenNotificationPanelClicked() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onNotificationPanelClicked()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(
+ Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true)
+ )
+ }
+
+ @Test
+ fun faceAuthIsRequestedWhenSwipeUpOnBouncer() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onSwipeUpOnBouncer()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, false))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index aa92177e863e..e4d8b2598fe3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -46,7 +46,6 @@ import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.CollectionUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -117,8 +116,6 @@ public class QSTileHostTest extends SysuiTestCase {
@Mock
private CustomTile mCustomTile;
@Mock
- private UiEventLogger mUiEventLogger;
- @Mock
private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
@Mock
@@ -164,7 +161,7 @@ public class QSTileHostTest extends SysuiTestCase {
saveSetting("");
mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
mPluginManager, mTunerService, mAutoTiles, mCentralSurfaces,
- mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
+ mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
mTileLifecycleManagerFactory, mUserFileManager, mFeatureFlags);
mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
@@ -684,14 +681,14 @@ public class QSTileHostTest extends SysuiTestCase {
QSFactory defaultFactory, Executor mainExecutor,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles,
- CentralSurfaces centralSurfaces, QSLogger qsLogger, UiEventLogger uiEventLogger,
+ CentralSurfaces centralSurfaces, QSLogger qsLogger,
UserTracker userTracker, SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
TileLifecycleManager.Factory tileLifecycleManagerFactory,
UserFileManager userFileManager, FeatureFlags featureFlags) {
super(context, defaultFactory, mainExecutor, pluginManager,
tunerService, autoTiles, Optional.of(centralSurfaces), qsLogger,
- uiEventLogger, userTracker, secureSettings, customTileStatePersister,
+ userTracker, secureSettings, customTileStatePersister,
tileLifecycleManagerFactory, userFileManager, featureFlags);
}
@@ -710,6 +707,7 @@ public class QSTileHostTest extends SysuiTestCase {
protected TestTile(QSHost host) {
super(
host,
+ mock(QsEventLogger.class),
mock(Looper.class),
mock(Handler.class),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt
new file mode 100644
index 000000000000..40aa2607dc94
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QsEventLoggerFake.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.InstanceIdSequenceFake
+
+class QsEventLoggerFake(
+ uiEventLogger: UiEventLoggerFake,
+ private val instanceIdSequence: InstanceIdSequenceFake,
+) : QsEventLogger, UiEventLogger by uiEventLogger {
+
+ val lastInstanceId: Int
+ get() = instanceIdSequence.lastInstanceId
+
+ override fun getNewInstanceId(): InstanceId {
+ return instanceIdSequence.newInstanceId()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index ac106ef9bf51..198ed4ac08fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.util.mockito.any
@@ -56,12 +57,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -89,6 +90,7 @@ class CustomTileTest : SysuiTestCase() {
@Mock private lateinit var applicationInfo: ApplicationInfo
@Mock private lateinit var serviceInfo: ServiceInfo
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
+ @Mock private lateinit var uiEventLogger: QsEventLogger
private var displayTracker = FakeDisplayTracker(mContext)
private lateinit var customTile: CustomTile
@@ -115,6 +117,7 @@ class CustomTileTest : SysuiTestCase() {
customTileBuilder = CustomTile.Builder(
{ tileHost },
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 36549fb826ec..962b53737274 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -61,6 +61,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.systemui.InstanceIdSequenceFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
@@ -69,6 +70,8 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.QsEventLoggerFake;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.StatusBarState;
@@ -106,7 +109,8 @@ public class QSTileImplTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
private UiEventLoggerFake mUiEventLoggerFake;
- private InstanceId mInstanceId = InstanceId.fakeInstanceId(5);
+ private QsEventLoggerFake mQsEventLoggerFake;
+ private InstanceId mInstanceId;
@Captor
private ArgumentCaptor<LogMaker> mLogCaptor;
@@ -115,18 +119,29 @@ public class QSTileImplTest extends SysuiTestCase {
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
+
mUiEventLoggerFake = new UiEventLoggerFake();
+ mQsEventLoggerFake =
+ new QsEventLoggerFake(mUiEventLoggerFake, new InstanceIdSequenceFake(10));
when(mHost.indexOf(SPEC)).thenReturn(POSITION);
when(mHost.getContext()).thenReturn(mContext);
- when(mHost.getUiEventLogger()).thenReturn(mUiEventLoggerFake);
- when(mHost.getNewInstanceId()).thenReturn(mInstanceId);
Handler mainHandler = new Handler(mTestableLooper.getLooper());
- mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager,
- mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
+ mTile = new TileImpl(
+ mHost,
+ mQsEventLoggerFake,
+ mTestableLooper.getLooper(),
+ mainHandler,
+ mFalsingManager,
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQsLogger
+ );
mTile.initialize();
mTestableLooper.processAllMessages();
+ mInstanceId = InstanceId.fakeInstanceId(mQsEventLoggerFake.getLastInstanceId());
mTile.setTileSpec(SPEC);
}
@@ -507,6 +522,7 @@ public class QSTileImplTest extends SysuiTestCase {
protected TileImpl(
QSHost host,
+ QsEventLogger uiEventLogger,
Looper backgroundLooper,
Handler mainHandler,
FalsingManager falsingManager,
@@ -515,7 +531,7 @@ public class QSTileImplTest extends SysuiTestCase {
ActivityStarter activityStarter,
QSLogger qsLogger
) {
- super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
getState().state = Tile.STATE_ACTIVE;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index 5e0190b65a12..c60cecb29d75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -22,8 +22,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -32,6 +30,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.settings.UserTracker
@@ -68,20 +67,21 @@ class AirplaneModeTileTest : SysuiTestCase() {
private lateinit var mGlobalSettings: GlobalSettings
@Mock
private lateinit var mUserTracker: UserTracker
+ @Mock
+ private lateinit var mUiEventLogger: QsEventLogger
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: AirplaneModeTile
- private val mUiEventLogger: UiEventLogger = UiEventLoggerFake()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
mTestableLooper = TestableLooper.get(this)
Mockito.`when`(mHost.context).thenReturn(mContext)
- Mockito.`when`(mHost.uiEventLogger).thenReturn(mUiEventLogger)
Mockito.`when`(mHost.userContext).thenReturn(mContext)
- mTile = AirplaneModeTile(mHost,
+ mTile = AirplaneModeTile(
+ mHost,
+ mUiEventLogger,
mTestableLooper.looper,
Handler(mTestableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index f1e3e8a71398..52b84559f396 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -9,12 +9,12 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
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.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.NextAlarmController
@@ -28,8 +28,8 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -52,7 +52,7 @@ class AlarmTileTest : SysuiTestCase() {
@Mock
private lateinit var nextAlarmController: NextAlarmController
@Mock
- private lateinit var uiEventLogger: UiEventLogger
+ private lateinit var uiEventLogger: QsEventLogger
@Mock
private lateinit var pendingIntent: PendingIntent
@Captor
@@ -67,10 +67,10 @@ class AlarmTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
`when`(qsHost.context).thenReturn(mContext)
- `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
tile = AlarmTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index a5c0004afe02..ff6814c06001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -31,6 +31,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BatteryController
@@ -43,10 +44,10 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -63,6 +64,8 @@ class BatterySaverTileTest : SysuiTestCase() {
@Mock
private lateinit var qsHost: QSHost
@Mock
+ private lateinit var uiEventLogger: QsEventLogger
+ @Mock
private lateinit var metricsLogger: MetricsLogger
@Mock
private lateinit var statusBarStateController: StatusBarStateController
@@ -90,6 +93,7 @@ class BatterySaverTileTest : SysuiTestCase() {
tile = BatterySaverTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 2e77de270c65..5e7f68ccf3d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -9,7 +9,6 @@ import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.settingslib.Utils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.systemui.R
@@ -20,6 +19,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BluetoothController
@@ -49,8 +49,8 @@ class BluetoothTileTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var bluetoothController: BluetoothController
+ @Mock private lateinit var uiEventLogger: QsEventLogger
- private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: FakeBluetoothTile
@@ -60,11 +60,11 @@ class BluetoothTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
whenever(qsHost.context).thenReturn(mContext)
- whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
tile =
FakeBluetoothTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
@@ -211,6 +211,7 @@ class BluetoothTileTest : SysuiTestCase() {
private class FakeBluetoothTile(
qsHost: QSHost,
+ uiEventLogger: QsEventLogger,
backgroundLooper: Looper,
mainHandler: Handler,
falsingManager: FalsingManager,
@@ -222,6 +223,7 @@ class BluetoothTileTest : SysuiTestCase() {
) :
BluetoothTile(
qsHost,
+ uiEventLogger,
backgroundLooper,
mainHandler,
falsingManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 41938541124a..70d82fdb6e5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -21,8 +21,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -30,6 +28,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLoggerFake
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
@@ -67,19 +66,21 @@ class CameraToggleTileTest : SysuiTestCase() {
private lateinit var privacyController: IndividualSensorPrivacyController
@Mock
private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var uiEventLogger: QsEventLoggerFake
private lateinit var testableLooper: TestableLooper
private lateinit var tile: CameraToggleTile
- private val uiEventLogger: UiEventLogger = UiEventLoggerFake()
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
whenever(host.context).thenReturn(mContext)
- whenever(host.uiEventLogger).thenReturn(uiEventLogger)
- tile = CameraToggleTile(host,
+ tile = CameraToggleTile(
+ host,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
metricsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 64fd09d5f5d9..93ed99423f0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -43,6 +43,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -94,6 +95,8 @@ public class CastTileTest extends SysuiTestCase {
private QSLogger mQSLogger;
@Mock
private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -107,6 +110,7 @@ public class CastTileTest extends SysuiTestCase {
mCastTile = new CastTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
index 13c30e9ea9ab..2250ef33f9b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
@@ -32,12 +32,12 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
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.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.FakeSettings;
@@ -67,7 +67,7 @@ public class ColorCorrectionTileTest extends SysuiTestCase {
@Mock
private QSLogger mQSLogger;
@Mock
- private UiEventLogger mUiEventLogger;
+ private QsEventLogger mUiEventLogger;
@Mock
private UserTracker mUserTracker;
@@ -83,10 +83,10 @@ public class ColorCorrectionTileTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
when(mHost.getContext()).thenReturn(mContext);
- when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
mTile = new ColorCorrectionTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index ff27e0255aa3..2e02bbe2ebf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -32,7 +32,6 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -40,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
@@ -72,7 +72,7 @@ public class ColorInversionTileTest extends SysuiTestCase {
@Mock
private QSLogger mQSLogger;
@Mock
- private UiEventLogger mUiEventLogger;
+ private QsEventLogger mUiEventLogger;
@Mock
private UserTracker mUserTracker;
@@ -88,10 +88,10 @@ public class ColorInversionTileTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
when(mHost.getContext()).thenReturn(mContext);
- when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
mTile = new ColorInversionTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index b048643aba84..176b33fa9fc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -21,7 +21,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
@@ -30,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.DataSaverController
@@ -57,8 +57,8 @@ class DataSaverTileTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var dataSaverController: DataSaverController
@Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+ @Mock private lateinit var uiEventLogger: QsEventLogger
- private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: DataSaverTile
@@ -68,11 +68,11 @@ class DataSaverTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
Mockito.`when`(mHost.context).thenReturn(mContext)
- Mockito.`when`(mHost.uiEventLogger).thenReturn(uiEventLogger)
tile =
DataSaverTile(
mHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index b51c378f6b6b..1346069d3c25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -27,7 +27,6 @@ import android.testing.TestableLooper
import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -44,6 +43,7 @@ import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.util.mockito.any
@@ -52,6 +52,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -59,15 +60,14 @@ import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.nullable
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
import java.util.Optional
-import org.junit.After
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -95,7 +95,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
@Mock
private lateinit var serviceInfo: ControlsServiceInfo
@Mock
- private lateinit var uiEventLogger: UiEventLogger
+ private lateinit var uiEventLogger: QsEventLogger
@Captor
private lateinit var listingCallbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -118,7 +118,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
spiedContext = spy(mContext)
doNothing().`when`(spiedContext).startActivity(any(Intent::class.java))
`when`(qsHost.context).thenReturn(spiedContext)
- `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(controlsComponent.isEnabled()).thenReturn(true)
`when`(controlsController.getPreferredSelection())
.thenReturn(SelectedItem.StructureItem(
@@ -399,6 +398,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
private fun createTile(): DeviceControlsTile {
return DeviceControlsTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index 6c0904eb9bfd..f0e4e3adda7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -28,7 +28,6 @@ import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
@@ -37,6 +36,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.ZenModeController
@@ -46,7 +46,6 @@ import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
-import java.io.File
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -55,8 +54,9 @@ import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.io.File
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -84,7 +84,7 @@ class DndTileTest : SysuiTestCase() {
private lateinit var qsLogger: QSLogger
@Mock
- private lateinit var uiEventLogger: UiEventLogger
+ private lateinit var uiEventLogger: QsEventLogger
@Mock
private lateinit var zenModeController: ZenModeController
@@ -109,7 +109,6 @@ class DndTileTest : SysuiTestCase() {
secureSettings = FakeSettings()
whenever(qsHost.userId).thenReturn(DEFAULT_USER)
- whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
val wrappedContext = object : ContextWrapper(context) {
override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
@@ -120,6 +119,7 @@ class DndTileTest : SysuiTestCase() {
tile = DndTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index 7d41aa6c3548..f231c6e56096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -48,6 +48,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
@@ -83,6 +84,8 @@ public class DreamTileTest extends SysuiTestCase {
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
@@ -258,6 +261,7 @@ public class DreamTileTest extends SysuiTestCase {
boolean dreamOnlyEnabledForSystemUser) {
return new DreamTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index 692a64422a7d..73aa6991c18a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -6,7 +6,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -14,6 +13,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.FlashlightController
@@ -45,7 +45,8 @@ class FlashlightTileTest : SysuiTestCase() {
@Mock private lateinit var flashlightController: FlashlightController
- private val uiEventLogger = UiEventLoggerFake()
+ @Mock private lateinit var uiEventLogger: QsEventLogger
+
private val falsingManager = FalsingManagerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: FlashlightTile
@@ -56,11 +57,11 @@ class FlashlightTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
Mockito.`when`(qsHost.context).thenReturn(mockContext)
- Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
tile =
FlashlightTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index eeebd4fb7792..1d6f225dd0a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -23,7 +23,6 @@ import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
@@ -31,7 +30,8 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -52,13 +52,13 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class FontScalingTileTest : SysuiTestCase() {
- @Mock private lateinit var qsHost: QSTileHost
+ @Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
- @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var uiEventLogger: QsEventLogger
private lateinit var testableLooper: TestableLooper
private lateinit var fontScalingTile: FontScalingTile
@@ -70,11 +70,11 @@ class FontScalingTileTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
`when`(qsHost.getContext()).thenReturn(mContext)
- `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
fontScalingTile =
FontScalingTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index 959e750ac5f4..73f61d0690e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -66,6 +67,8 @@ public class HotspotTileTest extends SysuiTestCase {
private HotspotController mHotspotController;
@Mock
private DataSaverController mDataSaverController;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private HotspotTile mTile;
@@ -80,6 +83,7 @@ public class HotspotTileTest extends SysuiTestCase {
mTile = new HotspotTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
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
index adfd7f71e8f8..7957c6a7cfb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -35,6 +35,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
@@ -63,6 +64,8 @@ public class InternetTileTest extends SysuiTestCase {
private AccessPointController mAccessPointController;
@Mock
private InternetDialogFactory mInternetDialogFactory;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private InternetTile mTile;
@@ -76,6 +79,7 @@ public class InternetTileTest extends SysuiTestCase {
mTile = new InternetTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 3642e874e7ae..0bf0b38f7471 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -22,7 +22,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -30,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
@@ -43,8 +43,8 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -71,8 +71,9 @@ class LocationTileTest : SysuiTestCase() {
private lateinit var keyguardStateController: KeyguardStateController
@Mock
private lateinit var panelInteractor: PanelInteractor
+ @Mock
+ private lateinit var uiEventLogger: QsEventLogger
- private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: LocationTile
@@ -80,10 +81,11 @@ class LocationTileTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
- `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(qsHost.context).thenReturn(mockContext)
- tile = LocationTile(qsHost,
+ tile = LocationTile(
+ qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index e2f64b2cc226..ceff546106f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -21,8 +21,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -30,6 +28,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
@@ -67,19 +66,22 @@ class MicrophoneToggleTileTest : SysuiTestCase() {
private lateinit var privacyController: IndividualSensorPrivacyController
@Mock
private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var uiEventLogger: QsEventLogger
private lateinit var testableLooper: TestableLooper
private lateinit var tile: MicrophoneToggleTile
- private val uiEventLogger: UiEventLogger = UiEventLoggerFake()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
whenever(host.context).thenReturn(mContext)
- whenever(host.uiEventLogger).thenReturn(uiEventLogger)
- tile = MicrophoneToggleTile(host,
+ tile = MicrophoneToggleTile(
+ host,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
metricsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index c7dae83e2056..763a7e5e4e06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -37,6 +37,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import org.junit.After;
@@ -70,6 +71,8 @@ public class NfcTileTest extends SysuiTestCase {
private QSLogger mQSLogger;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private NfcTile mNfcTile;
@@ -84,6 +87,7 @@ public class NfcTileTest extends SysuiTestCase {
mNfcTile = new NfcTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
index 04af69c84cf8..6c8f76b48f03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -23,8 +23,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -33,6 +31,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.LocationController
@@ -43,8 +42,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -68,17 +67,18 @@ class NightDisplayTileTest : SysuiTestCase() {
@Mock private lateinit var mNightDisplayListener: NightDisplayListener
+ @Mock private lateinit var mUiEventLogger: QsEventLogger
+
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: NightDisplayTile
- private val mUiEventLogger: UiEventLogger = UiEventLoggerFake()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
mTestableLooper = TestableLooper.get(this)
whenever(mHost.context).thenReturn(mContext)
- whenever(mHost.uiEventLogger).thenReturn(mUiEventLogger)
whenever(mHost.userContext).thenReturn(mContext)
whenever(mNightDisplayListenerBuilder.setUser(anyInt()))
.thenReturn(mNightDisplayListenerBuilder)
@@ -87,6 +87,7 @@ class NightDisplayTileTest : SysuiTestCase() {
mTile =
NightDisplayTile(
mHost,
+ mUiEventLogger,
mTestableLooper.looper,
Handler(mTestableLooper.looper),
FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
index 652c138f6478..c391153fecc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
@@ -33,6 +33,7 @@ import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
@@ -65,6 +66,8 @@ public class OneHandedModeTileTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private OneHandedModeTile mTile;
@@ -78,6 +81,7 @@ public class OneHandedModeTileTest extends SysuiTestCase {
mTile = spy(new OneHandedModeTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index 3125d455acfb..6f2d904dda64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -30,8 +30,6 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -40,6 +38,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -64,7 +63,8 @@ public class QRCodeScannerTileTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
@Mock
private QSLogger mQSLogger;
- private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
+ @Mock
+ private QsEventLogger mUiEventLogger;
@Mock
private QRCodeScannerController mController;
@@ -79,6 +79,7 @@ public class QRCodeScannerTileTest extends SysuiTestCase {
mTile = new QRCodeScannerTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 596df7856ee1..b089e380304d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -58,8 +58,6 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -67,6 +65,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -109,7 +108,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
@Mock
private QSLogger mQSLogger;
- private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
+ @Mock
+ private QsEventLogger mUiEventLogger;
@Mock
private QuickAccessWalletClient mQuickAccessWalletClient;
@Mock
@@ -136,7 +136,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
doNothing().when(mSpiedContext).startActivity(any(Intent.class));
when(mHost.getContext()).thenReturn(mSpiedContext);
- when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL);
when(mQuickAccessWalletClient.getTileIcon()).thenReturn(mTileIcon);
when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
@@ -146,6 +145,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mTile = new QuickAccessWalletTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 7913628c5693..d2445944c182 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -69,6 +70,8 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private ReduceBrightColorsController mReduceBrightColorsController;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private ReduceBrightColorsTile mTile;
@@ -85,6 +88,7 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
true,
mReduceBrightColorsController,
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index 5b94cfedaedf..e106741499ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -87,6 +88,8 @@ public class RotationLockTileTest extends SysuiTestCase {
DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
@Mock
RotationPolicyWrapper mRotationPolicyWrapper;
+ @Mock
+ QsEventLogger mUiEventLogger;
private RotationLockController mController;
private TestableLooper mTestableLooper;
@@ -105,6 +108,7 @@ public class RotationLockTileTest extends SysuiTestCase {
mLockTile = new RotationLockTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index d9ed1a299f51..fff2b8f5f8cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -44,6 +44,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -86,6 +87,8 @@ public class ScreenRecordTileTest extends SysuiTestCase {
private DialogLaunchAnimator mDialogLaunchAnimator;
@Mock
private PanelInteractor mPanelInteractor;
+ @Mock
+ private QsEventLogger mUiEventLogger;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -100,6 +103,7 @@ public class ScreenRecordTileTest extends SysuiTestCase {
mTile = new ScreenRecordTile(
mHost,
+ mUiEventLogger,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index b55657163382..79147e7e66b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -25,7 +25,6 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -33,6 +32,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BatteryController
@@ -63,8 +63,8 @@ class UiModeNightTileTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var batteryController: BatteryController
@Mock private lateinit var locationController: LocationController
+ @Mock private lateinit var uiEventLogger: QsEventLogger
- private val uiEventLogger = UiEventLoggerFake()
private val falsingManager = FalsingManagerFake()
private lateinit var testableLooper: TestableLooper
private lateinit var tile: UiModeNightTile
@@ -81,11 +81,11 @@ class UiModeNightTileTest : SysuiTestCase() {
`when`(qsHost.userContext).thenReturn(mContext)
`when`(mockContext.resources).thenReturn(resources)
`when`(resources.configuration).thenReturn(configuration)
- `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
tile =
UiModeNightTile(
qsHost,
+ uiEventLogger,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 5ca37716cbff..4bfd6a2e28f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -93,6 +93,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerReposito
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
@@ -665,7 +666,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mMetricsLogger,
mFeatureFlags,
mInteractionJankMonitor,
- mShadeLog
+ mShadeLog,
+ mock(KeyguardFaceAuthInteractor.class)
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index d8ffe39e427d..908f7cbf4801 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -62,6 +62,7 @@ import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
@@ -239,7 +240,8 @@ public class QuickSettingsControllerTest extends SysuiTestCase {
mMetricsLogger,
mFeatureFlags,
mInteractionJankMonitor,
- mShadeLogger
+ mShadeLogger,
+ mock(KeyguardFaceAuthInteractor.class)
);
mFragmentListener = mQsController.getQsFragmentListener();
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 7d022192f935..9186c35e2b47 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
@@ -100,7 +100,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.NOTIFICATION_ANIMATE_BIG_PICTURE, true);
fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false);
mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 14e5f9e63ebe..2cc375b3d0ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -24,7 +24,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@@ -68,4 +67,22 @@ class NotificationShelfInteractorTest : SysuiTestCase() {
assertThat(shelfStatic).isTrue()
}
+
+ @Test
+ fun shelfOnKeyguard_whenKeyguardShowing() = runTest {
+ val onKeyguard by collectLastValue(underTest.isShowingOnKeyguard)
+
+ keyguardRepository.setKeyguardShowing(true)
+
+ assertThat(onKeyguard).isTrue()
+ }
+
+ @Test
+ fun shelfNotOnKeyguard_whenKeyguardNotShowing() = runTest {
+ val onKeyguard by collectLastValue(underTest.isShowingOnKeyguard)
+
+ keyguardRepository.setKeyguardShowing(false)
+
+ assertThat(onKeyguard).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 6c5fb8bcff22..439edaf3faaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -69,4 +69,22 @@ class NotificationShelfViewModelTest : SysuiTestCase() {
assertThat(canModifyNotifColor).isFalse()
}
+
+ @Test
+ fun isClickable_whenKeyguardShowing() = runTest {
+ val isClickable by collectLastValue(underTest.isClickable)
+
+ keyguardRepository.setKeyguardShowing(true)
+
+ assertThat(isClickable).isTrue()
+ }
+
+ @Test
+ fun isNotClickable_whenKeyguardNotShowing() = runTest {
+ val isClickable by collectLastValue(underTest.isClickable)
+
+ keyguardRepository.setKeyguardShowing(false)
+
+ assertThat(isClickable).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index e6f10cdafe03..5279740a8702 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -23,6 +23,7 @@ import android.view.View.VISIBLE
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -48,6 +49,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
+ @Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var stackLayout: NotificationStackScrollLayout
private val testableResources = mContext.orCreateTestableResources
@@ -67,7 +69,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
NotificationStackSizeCalculator(
statusBarStateController = sysuiStatusBarStateController,
lockscreenShadeTransitionController = lockscreenShadeTransitionController,
- testableResources.resources)
+ mediaDataManager = mediaDataManager,
+ testableResources.resources
+ )
}
@Test
@@ -76,7 +80,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val maxNotifications =
computeMaxKeyguardNotifications(
- rows, spaceForNotifications = 0f, spaceForShelf = 0f, shelfHeight = 0f)
+ rows,
+ spaceForNotifications = 0f,
+ spaceForShelf = 0f,
+ shelfHeight = 0f
+ )
assertThat(maxNotifications).isEqualTo(0)
}
@@ -91,7 +99,8 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
rows,
spaceForNotifications = Float.MAX_VALUE,
spaceForShelf = Float.MAX_VALUE,
- shelfHeight)
+ shelfHeight
+ )
assertThat(maxNotifications).isEqualTo(numberOfRows)
}
@@ -111,6 +120,28 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
+ fun computeMaxKeyguardNotifications_onLockscreenSpaceForMinHeightButNotIntrinsicHeight_returnsOne() {
+ setGapHeight(0f)
+ // No divider height since we're testing one element where index = 0
+
+ whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(0f)
+
+ val row = createMockRow(10f, isSticky = true)
+ whenever(row.getMinHeight(any())).thenReturn(5)
+
+ val maxNotifications =
+ computeMaxKeyguardNotifications(
+ listOf(row),
+ /* spaceForNotifications= */ 5f,
+ /* spaceForShelf= */ 0f,
+ /* shelfHeight= */ 0f
+ )
+
+ assertThat(maxNotifications).isEqualTo(1)
+ }
+
+ @Test
fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
setGapHeight(gapHeight)
val shelfHeight = shelfHeight + dividerHeight
@@ -126,7 +157,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
val maxNotifications =
computeMaxKeyguardNotifications(
- rows, spaceForNotifications + 1, spaceForShelf, shelfHeight)
+ rows,
+ spaceForNotifications + 1,
+ spaceForShelf,
+ shelfHeight
+ )
assertThat(maxNotifications).isEqualTo(2)
}
@@ -136,24 +171,23 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
// Each row in separate section.
setGapHeight(gapHeight)
- val spaceForNotifications =
+ val notifSpace =
listOf(
rowHeight,
dividerHeight + gapHeight + rowHeight,
)
.sum()
- val spaceForShelf = dividerHeight + gapHeight + shelfHeight
- val spaceUsed = spaceForNotifications + spaceForShelf
+ val shelfSpace = dividerHeight + gapHeight + shelfHeight
+ val spaceUsed = notifSpace + shelfSpace
val rows =
listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
val maxNotifications =
- computeMaxKeyguardNotifications(rows, spaceForNotifications, spaceForShelf, shelfHeight)
+ computeMaxKeyguardNotifications(rows, notifSpace, shelfSpace, shelfHeight)
assertThat(maxNotifications).isEqualTo(2)
- val height =
- sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
+ val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
assertThat(height).isEqualTo(spaceUsed)
}
@@ -170,11 +204,14 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
// test that we only use space required
val maxNotifications =
computeMaxKeyguardNotifications(
- rows, spaceForNotifications + 1, spaceForShelf, shelfHeight)
+ rows,
+ spaceForNotifications + 1,
+ spaceForShelf,
+ shelfHeight
+ )
assertThat(maxNotifications).isEqualTo(1)
- val height =
- sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
+ val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
assertThat(height).isEqualTo(spaceUsed)
}
@@ -200,60 +237,101 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
}
@Test
- fun spaceNeeded_onLockscreen_usesMinHeight() {
+ fun getSpaceNeeded_onLockscreenEnoughSpaceStickyHun_intrinsicHeight() {
setGapHeight(0f)
// No divider height since we're testing one element where index = 0
- val expandableView = createMockRow(rowHeight)
+ val row = createMockRow(10f, isSticky = true)
+ whenever(row.getMinHeight(any())).thenReturn(5)
+
+ val space =
+ sizeCalculator.getSpaceNeeded(
+ row,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true
+ )
+ assertThat(space.whenEnoughSpace).isEqualTo(10f)
+ }
+
+ @Test
+ fun getSpaceNeeded_onLockscreenEnoughSpaceNotStickyHun_minHeight() {
+ setGapHeight(0f)
+ // No divider height since we're testing one element where index = 0
+
+ val row = createMockRow(rowHeight)
+ whenever(row.heightWithoutLockscreenConstraints).thenReturn(10)
+ whenever(row.getMinHeight(any())).thenReturn(5)
+
+ val space =
+ sizeCalculator.getSpaceNeeded(
+ row,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true
+ )
+ assertThat(space.whenEnoughSpace).isEqualTo(5)
+ }
+
+ @Test
+ fun getSpaceNeeded_onLockscreenSavingSpaceStickyHun_minHeight() {
+ setGapHeight(0f)
+ // No divider height since we're testing one element where index = 0
+
+ val expandableView = createMockRow(10f, isSticky = true)
whenever(expandableView.getMinHeight(any())).thenReturn(5)
- whenever(expandableView.intrinsicHeight).thenReturn(10)
val space =
- sizeCalculator.spaceNeeded(
+ sizeCalculator.getSpaceNeeded(
expandableView,
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = true)
- assertThat(space).isEqualTo(5)
+ onLockscreen = true
+ )
+ assertThat(space.whenSavingSpace).isEqualTo(5)
}
@Test
- fun spaceNeeded_fsiHunOnLockscreen_usesIntrinsicHeight() {
+ fun getSpaceNeeded_onLockscreenSavingSpaceNotStickyHun_minHeight() {
setGapHeight(0f)
// No divider height since we're testing one element where index = 0
- val expandableView = createMockStickyRow(rowHeight)
+ val expandableView = createMockRow(rowHeight)
whenever(expandableView.getMinHeight(any())).thenReturn(5)
whenever(expandableView.intrinsicHeight).thenReturn(10)
val space =
- sizeCalculator.spaceNeeded(
- expandableView,
- visibleIndex = 0,
- previousView = null,
- stack = stackLayout,
- onLockscreen = true)
- assertThat(space).isEqualTo(10)
+ sizeCalculator.getSpaceNeeded(
+ expandableView,
+ visibleIndex = 0,
+ previousView = null,
+ stack = stackLayout,
+ onLockscreen = true
+ )
+ assertThat(space.whenSavingSpace).isEqualTo(5)
}
@Test
- fun spaceNeeded_notOnLockscreen_usesIntrinsicHeight() {
+ fun getSpaceNeeded_notOnLockscreen_intrinsicHeight() {
setGapHeight(0f)
// No divider height since we're testing one element where index = 0
val expandableView = createMockRow(rowHeight)
- whenever(expandableView.getMinHeight(any())).thenReturn(5)
- whenever(expandableView.intrinsicHeight).thenReturn(10)
+ whenever(expandableView.getMinHeight(any())).thenReturn(1)
val space =
- sizeCalculator.spaceNeeded(
+ sizeCalculator.getSpaceNeeded(
expandableView,
visibleIndex = 0,
previousView = null,
stack = stackLayout,
- onLockscreen = false)
- assertThat(space).isEqualTo(10)
+ onLockscreen = false
+ )
+ assertThat(space.whenEnoughSpace).isEqualTo(rowHeight)
+ assertThat(space.whenSavingSpace).isEqualTo(rowHeight)
}
private fun computeMaxKeyguardNotifications(
@@ -264,7 +342,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
): Int {
setupChildren(rows)
return sizeCalculator.computeMaxKeyguardNotifications(
- stackLayout, spaceForNotifications, spaceForShelf, shelfHeight)
+ stackLayout,
+ spaceForNotifications,
+ spaceForShelf,
+ shelfHeight
+ )
}
private fun setupChildren(children: List<ExpandableView>) {
@@ -280,30 +362,13 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
private fun createMockRow(
height: Float = rowHeight,
+ isSticky: Boolean = false,
isRemoved: Boolean = false,
- visibility: Int = VISIBLE
- ): ExpandableNotificationRow {
- val row = mock(ExpandableNotificationRow::class.java)
- val entry = mock(NotificationEntry::class.java)
- val sbn = mock(StatusBarNotification::class.java)
- whenever(entry.sbn).thenReturn(sbn)
- whenever(row.entry).thenReturn(entry)
- whenever(row.isRemoved).thenReturn(isRemoved)
- whenever(row.visibility).thenReturn(visibility)
- whenever(row.getMinHeight(any())).thenReturn(height.toInt())
- whenever(row.intrinsicHeight).thenReturn(height.toInt())
- return row
- }
-
- private fun createMockStickyRow(
- height: Float = rowHeight,
- isRemoved: Boolean = false,
- visibility: Int = VISIBLE
+ visibility: Int = VISIBLE,
): ExpandableNotificationRow {
val row = mock(ExpandableNotificationRow::class.java)
val entry = mock(NotificationEntry::class.java)
- whenever(entry.isStickyAndNotDemoted).thenReturn(true)
-
+ whenever(entry.isStickyAndNotDemoted).thenReturn(isSticky)
val sbn = mock(StatusBarNotification::class.java)
whenever(entry.sbn).thenReturn(sbn)
whenever(row.entry).thenReturn(entry)
@@ -311,6 +376,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() {
whenever(row.visibility).thenReturn(visibility)
whenever(row.getMinHeight(any())).thenReturn(height.toInt())
whenever(row.intrinsicHeight).thenReturn(height.toInt())
+ whenever(row.heightWithoutLockscreenConstraints).thenReturn(height.toInt())
return row
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 746c92e485b7..02c459b11d07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -16,12 +16,10 @@
package com.android.systemui.statusbar.phone
-import android.animation.Animator
import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
-import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
@@ -39,10 +37,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
@@ -111,27 +107,6 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
controller.onStartedWakingUp()
}
- @Test
- fun testAnimClearsEndListener() {
- val keyguardView = View(context)
- val animator = spy(keyguardView.animate())
- val keyguardSpy = spy(keyguardView)
- Mockito.`when`(keyguardSpy.animate()).thenReturn(animator)
- val listener = ArgumentCaptor.forClass(Animator.AnimatorListener::class.java)
- val endAction = ArgumentCaptor.forClass(Runnable::class.java)
- controller.animateInKeyguard(keyguardSpy, Runnable {})
- Mockito.verify(animator).setListener(listener.capture())
- Mockito.verify(animator).withEndAction(endAction.capture())
-
- // Verify that the listener is cleared if we cancel it.
- listener.value.onAnimationCancel(null)
- Mockito.verify(animator).setListener(null)
-
- // Verify that the listener is also cleared if the end action is triggered.
- endAction.value.run()
- verify(animator, times(2)).setListener(null)
- }
-
/**
* The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
* animation to start. If the device is woken up during the screen off, we should *never* do
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index ddb7f4d88d30..01bec879102d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -32,9 +32,8 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -69,14 +68,8 @@ class MobileIconsViewModelTest : SysuiTestCase() {
FakeConnectivityRepository(),
)
- val subscriptionIdsFlow =
- interactor.filteredSubscriptions
- .map { subs -> subs.map { it.subscriptionId } }
- .stateIn(testScope.backgroundScope, SharingStarted.WhileSubscribed(), listOf())
-
underTest =
MobileIconsViewModel(
- subscriptionIdsFlow,
logger,
verboseLogger,
interactor,
@@ -90,6 +83,32 @@ class MobileIconsViewModelTest : SysuiTestCase() {
}
@Test
+ fun subscriptionIdsFlow_matchesInteractor() =
+ testScope.runTest {
+ var latest: List<Int>? = null
+ val job = underTest.subscriptionIdsFlow.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value =
+ listOf(
+ SubscriptionModel(subscriptionId = 1, isOpportunistic = false),
+ )
+ assertThat(latest).isEqualTo(listOf(1))
+
+ interactor.filteredSubscriptions.value =
+ listOf(
+ SubscriptionModel(subscriptionId = 2, isOpportunistic = false),
+ SubscriptionModel(subscriptionId = 5, isOpportunistic = true),
+ SubscriptionModel(subscriptionId = 7, isOpportunistic = true),
+ )
+ assertThat(latest).isEqualTo(listOf(2, 5, 7))
+
+ interactor.filteredSubscriptions.value = emptyList()
+ assertThat(latest).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
fun `caching - mobile icon view model is reused for same sub id`() =
testScope.runTest {
val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
new file mode 100644
index 000000000000..9cf3e443320d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import static android.media.AudioManager.CSD_WARNING_DOSE_REACHED_1X;
+import static android.media.AudioManager.CSD_WARNING_DOSE_REPEATED_5X;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.media.AudioManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CsdWarningDialogTest extends SysuiTestCase {
+
+ private NotificationManager mNotificationManager;
+ private AudioManager mAudioManager;
+
+ @Before
+ public void setup() {
+ mNotificationManager = mock(NotificationManager.class);
+ getContext().addMockSystemService(NotificationManager.class, mNotificationManager);
+
+ mAudioManager = mock(AudioManager.class);
+ getContext().addMockSystemService(AudioManager.class, mAudioManager);
+ }
+
+ @Test
+ public void create1XCsdDialogAndWait_sendsNotification() {
+ FakeExecutor executor = new FakeExecutor(new FakeSystemClock());
+ // instantiate directly instead of via factory; we don't want executor to be @Background
+ CsdWarningDialog dialog = new CsdWarningDialog(CSD_WARNING_DOSE_REACHED_1X, mContext,
+ mAudioManager, mNotificationManager, executor, null);
+
+ dialog.show();
+ executor.advanceClockToLast();
+ executor.runAllReady();
+ dialog.dismiss();
+
+ verify(mNotificationManager).notify(
+ eq(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO), any(Notification.class));
+ }
+
+ @Test
+ public void create5XCsdDiSalogAndWait_willNotSendNotification() {
+ FakeExecutor executor = new FakeExecutor(new FakeSystemClock());
+ CsdWarningDialog dialog = new CsdWarningDialog(CSD_WARNING_DOSE_REPEATED_5X, mContext,
+ mAudioManager, mNotificationManager, executor, null);
+
+ dialog.show();
+ executor.advanceClockToLast();
+ executor.runAllReady();
+ dialog.dismiss();
+
+ verify(mNotificationManager, never()).notify(
+ eq(SystemMessageProto.SystemMessage.NOTE_CSD_LOWER_AUDIO), any(Notification.class));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index d419095921b8..eb2688894cb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -102,6 +102,15 @@ public class VolumeDialogImplTest extends SysuiTestCase {
InteractionJankMonitor mInteractionJankMonitor;
@Mock
private DumpManager mDumpManager;
+ @Mock CsdWarningDialog mCsdWarningDialog;
+
+ private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
+ new CsdWarningDialog.Factory() {
+ @Override
+ public CsdWarningDialog create(int warningType, Runnable onCleanup) {
+ return mCsdWarningDialog;
+ }
+ };
@Before
public void setup() throws Exception {
@@ -124,6 +133,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mInteractionJankMonitor,
mDeviceConfigProxy,
mExecutor,
+ mCsdWarningDialogFactory,
mDumpManager
);
mDialog.init(0, null);
@@ -352,6 +362,14 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mDialog.getDialogView().animate().cancel();
}
+ @Test
+ public void showCsdWarning_dialogShown() {
+ mDialog.showCsdWarningH(AudioManager.CSD_WARNING_DOSE_REACHED_1X,
+ CsdWarningDialog.NO_ACTION_TIMEOUT_MS);
+
+ verify(mCsdWarningDialog).show();
+ }
+
/*
@Test
public void testContentDescriptions() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index c08ecd0e3b0c..738f09ddce3d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.map
class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
@@ -46,14 +45,19 @@ class FakeDeviceEntryFaceAuthRepository : DeviceEntryFaceAuthRepository {
private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null)
val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
_runningAuthRequest.asStateFlow()
- override val isAuthRunning = _runningAuthRequest.map { it != null }
+
+ private val _isAuthRunning = MutableStateFlow(false)
+ override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
+
override val isBypassEnabled = MutableStateFlow(false)
override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
_runningAuthRequest.value = uiEvent to fallbackToDetection
+ _isAuthRunning.value = true
}
override fun cancel() {
+ _isAuthRunning.value = false
_runningAuthRequest.value = null
}
}
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index cbca3f03fb0f..a41d0e57cd21 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -32,6 +32,7 @@ LOCAL_REQUIRED_MODULES := \
NavigationBarModeGesturalOverlayWideBack \
NavigationBarModeGesturalOverlayExtraWideBack \
TransparentNavigationBarOverlay \
+ NotesRoleEnabledOverlay \
preinstalled-packages-platform-overlays.xml
include $(BUILD_PHONY_PACKAGE)
diff --git a/core/tests/expresslog/Android.bp b/packages/overlays/NotesRoleEnabledOverlay/Android.bp
index cab49a76a734..68ebd9652399 100644
--- a/core/tests/expresslog/Android.bp
+++ b/packages/overlays/NotesRoleEnabledOverlay/Android.bp
@@ -1,16 +1,18 @@
-// Copyright (C) 2023 The Android Open Source Project
+//
+// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+//
package {
// See: http://go/android-license-faq
@@ -21,27 +23,8 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-android_test {
- name: "ExpressLogTests",
-
- srcs: [
- "src/**/*.java",
- ],
-
- static_libs: [
- "androidx.test.rules",
- "modules-utils-build",
- ],
-
- libs: [
- "android.test.base",
- "android.test.runner",
- ],
-
- platform_apis: true,
- test_suites: [
- "general-tests",
- ],
-
- certificate: "platform",
+runtime_resource_overlay {
+ name: "NotesRoleEnabledOverlay",
+ theme: "NotesRoleEnabled",
+ product_specific: true,
}
diff --git a/packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml b/packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml
new file mode 100644
index 000000000000..c01178d1727d
--- /dev/null
+++ b/packages/overlays/NotesRoleEnabledOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.role.notes.enabled"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:priority="1"/>
+
+ <application android:label="@string/notes_role_enabled_overlay_title" android:hasCode="false"/>
+</manifest> \ No newline at end of file
diff --git a/packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml b/packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml
new file mode 100644
index 000000000000..f27f5f42ae6b
--- /dev/null
+++ b/packages/overlays/NotesRoleEnabledOverlay/res/values/config.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <!-- Whether the default notes role should be enabled. In builds without
+ device-specific overlays, this can be controlled in developer options. -->
+ <bool name="config_enableDefaultNotes">true</bool>
+
+ <!-- Whether the default notes role for work profile should be enabled.
+ In builds without device-specific overlays, this can be controlled in
+ developer options. -->
+ <bool name="config_enableDefaultNotesForWorkProfile">true</bool>
+</resources>
diff --git a/packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml b/packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml
new file mode 100644
index 000000000000..3edbb571c4d1
--- /dev/null
+++ b/packages/overlays/NotesRoleEnabledOverlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Name of overlay [CHAR LIMIT=64] -->
+ <string name="notes_role_enabled_overlay_title" translatable="false">Notes Role enabled</string>
+</resources> \ No newline at end of file
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 47027342974d..21d09792f1c7 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -402,5 +402,7 @@ message SystemMessage {
// Package: android
NOTE_ALL_MANAGED_SUBSCRIPTIONS_AND_MANAGED_PROFILE_OFF = 1006;
+ // Notify the user that audio was lowered based on Calculated Sound Dose (CSD)
+ NOTE_CSD_LOWER_AUDIO = 1007;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 51325e72204d..0bdb0c80d219 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -112,6 +112,7 @@ import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.IWindow;
import android.view.InputDevice;
@@ -160,6 +161,7 @@ import com.android.server.accessibility.magnification.WindowMagnificationManager
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import com.android.settingslib.RestrictedLockUtils;
@@ -301,7 +303,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables =
new ArrayList<>();
- private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ @GuardedBy("mLock")
+ private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM;
+
+ // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation:
+ // when the UiAutomation is set in a visible background user, mCurrentUserId points to that user
+ // and mRealCurrentUserId points to the "real" current user; otherwise, mRealCurrentUserId
+ // is set as UserHandle.USER_CURRENT.
+ @GuardedBy("mLock")
+ private @UserIdInt int mRealCurrentUserId = UserHandle.USER_CURRENT;
+
+ // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation
+ // purposes - in the long term, the whole service should be refactored so it handles "visible"
+ // users, not current user. Notice that because this is temporary, it's not trying to optimize
+ // performance / utilization (for example, it's not using an IntArray)
+ @GuardedBy("mLock")
+ @Nullable // only set when device supports visible background users
+ private final SparseBooleanArray mVisibleBgUserIds;
//TODO: Remove this hack
private boolean mInitialized;
@@ -316,6 +334,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private SparseArray<SurfaceControl> mA11yOverlayLayers = new SparseArray<>();
private final FlashNotificationsController mFlashNotificationsController;
+ private final UserManagerInternal mUmi;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -445,6 +464,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mHasInputFilter = true;
}
mFlashNotificationsController = new FlashNotificationsController(mContext);
+ mUmi = LocalServices.getService(UserManagerInternal.class);
+ // TODO(b/255426725): not used on tests
+ mVisibleBgUserIds = null;
+
init();
}
@@ -477,6 +500,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext, mMainHandler,
mUiAutomationManager, this);
mFlashNotificationsController = new FlashNotificationsController(mContext);
+ mUmi = LocalServices.getService(UserManagerInternal.class);
+
+ if (UserManager.isVisibleBackgroundUsersEnabled()) {
+ mVisibleBgUserIds = new SparseBooleanArray();
+ mUmi.addUserVisibilityListener((u, v) -> onUserVisibilityChanged(u, v));
+ } else {
+ mVisibleBgUserIds = null;
+ }
+
init();
}
@@ -493,6 +525,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return mCurrentUserId;
}
+ @GuardedBy("mLock")
+ @Override
+ public SparseBooleanArray getVisibleUserIdsLocked() {
+ return mVisibleBgUserIds;
+ }
+
@Override
public boolean isAccessibilityButtonShown() {
return mIsAccessibilityButtonShown;
@@ -1362,6 +1400,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void registerUiTestAutomationService(IBinder owner,
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
+ int userId,
int flags) {
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService",
@@ -1374,6 +1413,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
synchronized (mLock) {
+ changeCurrentUserForTestAutomationIfNeededLocked(userId);
mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
mSecurityPolicy, this, getTraceManager(), mWindowManagerService,
@@ -1390,7 +1430,47 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
+ restoreCurrentUserAfterTestAutomationIfNeededLocked();
+ }
+ }
+
+ // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation
+ @GuardedBy("mLock")
+ private void changeCurrentUserForTestAutomationIfNeededLocked(@UserIdInt int userId) {
+ if (mVisibleBgUserIds == null) {
+ Slogf.d(LOG_TAG, "changeCurrentUserForTestAutomationIfNeededLocked(%d): ignoring "
+ + "because device doesn't support visible background users", userId);
+ return;
+ }
+ if (!mVisibleBgUserIds.get(userId)) {
+ Slogf.wtf(LOG_TAG, "Cannot change current user to %d as it's not visible "
+ + "(mVisibleUsers=%s)", userId, mVisibleBgUserIds);
+ return;
+ }
+ if (mCurrentUserId == userId) {
+ Slogf.w(LOG_TAG, "NOT changing current user for test automation purposes as it is "
+ + "already %d", mCurrentUserId);
+ return;
+ }
+ Slogf.i(LOG_TAG, "Changing current user from %d to %d for test automation purposes",
+ mCurrentUserId, userId);
+ mRealCurrentUserId = mCurrentUserId;
+ switchUser(userId);
+ }
+
+ // TODO(b/255426725): temporary workaround to support visible background users for UiAutomation
+ @GuardedBy("mLock")
+ private void restoreCurrentUserAfterTestAutomationIfNeededLocked() {
+ if (mVisibleBgUserIds == null) {
+ Slogf.d(LOG_TAG, "restoreCurrentUserForTestAutomationIfNeededLocked(): ignoring "
+ + "because device doesn't support visible background users");
+ return;
}
+ Slogf.i(LOG_TAG, "Restoring current user to %d after using %d for test automation purposes",
+ mRealCurrentUserId, mCurrentUserId);
+ int currentUserId = mRealCurrentUserId;
+ mRealCurrentUserId = UserHandle.USER_CURRENT;
+ switchUser(currentUserId);
}
@Override
@@ -2291,8 +2371,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void updateServicesLocked(AccessibilityUserState userState) {
Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
userState.mComponentNameToServiceMap;
- boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class)
- .isUserUnlockingOrUnlocked(userState.mUserId);
+ boolean isUnlockingOrUnlocked = mUmi.isUserUnlockingOrUnlocked(userState.mUserId);
for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
@@ -2593,6 +2672,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ private void onUserVisibilityChanged(@UserIdInt int userId, boolean visible) {
+ if (DEBUG) {
+ Slogf.d(LOG_TAG, "onUserVisibilityChanged(): %d => %b", userId, visible);
+ }
+ synchronized (mLock) {
+ if (visible) {
+ mVisibleBgUserIds.put(userId, visible);
+ } else {
+ mVisibleBgUserIds.delete(userId);
+ }
+ }
+ }
+
/**
* Called when any property of the user state has changed.
*
@@ -4025,7 +4117,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
pw.println();
pw.append("currentUserId=").append(String.valueOf(mCurrentUserId));
+ if (mRealCurrentUserId != UserHandle.USER_CURRENT
+ && mCurrentUserId != mRealCurrentUserId) {
+ pw.append(" (set for UiAutomation purposes; \"real\" current user is ")
+ .append(String.valueOf(mRealCurrentUserId)).append(")");
+ }
pw.println();
+ if (mVisibleBgUserIds != null) {
+ pw.append("visibleBgUserIds=").append(mVisibleBgUserIds.toString());
+ pw.println();
+ }
pw.append("hasWindowMagnificationConnection=").append(
String.valueOf(getWindowMagnificationMgr().isConnected()));
pw.println();
@@ -4052,6 +4153,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
pw.println();
mProxyManager.dump(fd, pw, args);
+ mA11yDisplayListener.dump(fd, pw, args);
}
}
@@ -4437,6 +4539,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
/* do nothing */
}
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Accessibility Display Listener:");
+ pw.println(" SystemUI uid: " + mSystemUiUid);
+ int size = mDisplaysList.size();
+ pw.printf(" %d valid display%s: ", size, (size == 1 ? "" : "s"));
+ for (int i = 0; i < size; i++) {
+ pw.print(mDisplaysList.get(i).getDisplayId());
+ if (i < size - 1) {
+ pw.print(", ");
+ }
+ }
+ pw.println();
+ }
+
private boolean isValidDisplay(@Nullable Display display) {
if (display == null || display.getType() == Display.TYPE_OVERLAY) {
return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index c37ea501bbc9..88656239e59b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -39,6 +39,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.InputMethodInfo;
@@ -88,6 +89,12 @@ public class AccessibilitySecurityPolicy {
*/
int getCurrentUserIdLocked();
// TODO: Should include resolveProfileParentLocked, but that was already in SecurityPolicy
+
+ // TODO(b/255426725): temporary hack; see comment on A11YMS.mVisibleBgUserIds
+ /**
+ * Returns the {@link android.os.UserManager#getVisibleUsers() visible users}.
+ */
+ @Nullable SparseBooleanArray getVisibleUserIdsLocked();
}
private final Context mContext;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index baed181ebd43..a8a536590004 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -51,6 +51,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilitySecurityPolicy.AccessibilityUserManager;
+import com.android.server.utils.Slogf;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -59,6 +60,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
/**
* This class provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and
@@ -67,6 +69,7 @@ import java.util.List;
public class AccessibilityWindowManager {
private static final String LOG_TAG = "AccessibilityWindowManager";
private static final boolean DEBUG = false;
+ private static final boolean VERBOSE = false;
private static int sNextWindowId;
@@ -209,6 +212,9 @@ public class AccessibilityWindowManager {
* Constructor for DisplayWindowsObserver.
*/
DisplayWindowsObserver(int displayId) {
+ if (DEBUG) {
+ Slogf.d(LOG_TAG, "Creating DisplayWindowsObserver for displayId %d", displayId);
+ }
mDisplayId = displayId;
}
@@ -430,12 +436,27 @@ public class AccessibilityWindowManager {
synchronized (mLock) {
updateWindowsByWindowAttributesLocked(windows);
if (DEBUG) {
- Slog.i(LOG_TAG, "Display Id = " + mDisplayId);
- Slog.i(LOG_TAG, "Windows changed: " + windows);
+ Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, "
+ + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
+ mAccessibilityUserManager.getCurrentUserIdLocked(),
+ mAccessibilityUserManager.getVisibleUserIdsLocked());
+ if (VERBOSE) {
+ Slogf.i(LOG_TAG, "%d windows changed: %s ", windows.size(), windows);
+ } else {
+ List<String> windowsInfo = windows.stream()
+ .map(w -> "{displayId=" + w.displayId + ", title=" + w.title + "}")
+ .collect(Collectors.toList());
+ Slogf.i(LOG_TAG, "%d windows changed: %s", windows.size(), windowsInfo);
+ }
}
if (shouldUpdateWindowsLocked(forceSend, windows)) {
mTopFocusedDisplayId = topFocusedDisplayId;
mTopFocusedWindowToken = topFocusedWindowToken;
+ if (DEBUG) {
+ Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for "
+ + "display %d and token %s",
+ topFocusedDisplayId, topFocusedWindowToken);
+ }
cacheWindows(windows);
// Lets the policy update the focused and active windows.
updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(),
@@ -443,6 +464,11 @@ public class AccessibilityWindowManager {
// Someone may be waiting for the windows - advertise it.
mLock.notifyAll();
}
+ else if (DEBUG) {
+ Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for "
+ + "display %d and token %s",
+ topFocusedDisplayId, topFocusedWindowToken);
+ }
}
}
@@ -472,6 +498,12 @@ public class AccessibilityWindowManager {
}
final int windowCount = windows.size();
+ if (VERBOSE) {
+ Slogf.v(LOG_TAG,
+ "shouldUpdateWindowsLocked(): mDisplayId=%d, windowCount=%d, "
+ + "mCachedWindowInfos.size()=%d, windows.size()=%d", mDisplayId,
+ windowCount, mCachedWindowInfos.size(), windows.size());
+ }
// We computed the windows and if they changed notify the client.
if (mCachedWindowInfos.size() != windowCount) {
// Different size means something changed.
@@ -1274,7 +1306,7 @@ public class AccessibilityWindowManager {
*/
@Nullable
public RemoteAccessibilityConnection getConnectionLocked(int userId, int windowId) {
- if (DEBUG) {
+ if (VERBOSE) {
Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
}
RemoteAccessibilityConnection connection = mGlobalInteractionConnections.get(windowId);
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 3ecf93328219..8fc30e43d1a0 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -80,7 +80,7 @@ import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ApkSigningBlockUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.expresslog.Histogram;
+import com.android.modules.expresslog.Histogram;
import com.android.internal.os.IBinaryTransparencyService;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.ApexManager;
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 92889c05d9f4..d256aead97e8 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -426,7 +426,7 @@ public class PackageWatchdog {
}
int impact = registeredObserver.onHealthCheckFailed(
versionedPackage, failureReason, mitigationCount);
- if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+ if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
@@ -466,7 +466,7 @@ public class PackageWatchdog {
if (registeredObserver != null) {
int impact = registeredObserver.onHealthCheckFailed(
failingPackage, failureReason, 1);
- if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+ if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
@@ -494,7 +494,7 @@ public class PackageWatchdog {
PackageHealthObserver registeredObserver = observer.registeredObserver;
if (registeredObserver != null) {
int impact = registeredObserver.onBootLoop(mitigationCount);
- if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+ if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0
&& impact < currentObserverImpact) {
currentObserverToNotify = registeredObserver;
currentObserverImpact = impact;
@@ -576,19 +576,23 @@ public class PackageWatchdog {
/** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
@Retention(SOURCE)
- @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE,
- PackageHealthObserverImpact.USER_IMPACT_LOW,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM,
- PackageHealthObserverImpact.USER_IMPACT_HIGH})
+ @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100})
public @interface PackageHealthObserverImpact {
/** No action to take. */
- int USER_IMPACT_NONE = 0;
+ int USER_IMPACT_LEVEL_0 = 0;
/* Action has low user impact, user of a device will barely notice. */
- int USER_IMPACT_LOW = 1;
- /* Action has medium user impact, user of a device will likely notice. */
- int USER_IMPACT_MEDIUM = 3;
+ int USER_IMPACT_LEVEL_10 = 10;
+ /* Actions having medium user impact, user of a device will likely notice. */
+ int USER_IMPACT_LEVEL_30 = 30;
+ int USER_IMPACT_LEVEL_50 = 50;
+ int USER_IMPACT_LEVEL_70 = 70;
/* Action has high user impact, a last resort, user of a device will be very frustrated. */
- int USER_IMPACT_HIGH = 5;
+ int USER_IMPACT_LEVEL_100 = 100;
}
/** Register instances of this interface to receive notifications on package failure. */
@@ -633,7 +637,7 @@ public class PackageWatchdog {
* boot loop (including this time).
*/
default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) {
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
/**
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 3de65f94decf..6e2e5f75b8f3 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -107,7 +107,7 @@ public class RescueParty {
static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG =
"namespace_to_package_mapping";
@VisibleForTesting
- static final long FACTORY_RESET_THROTTLE_DURATION_MS = TimeUnit.MINUTES.toMillis(10);
+ static final long DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN = 10;
private static final String NAME = "rescue-party-observer";
@@ -117,6 +117,8 @@ public class RescueParty {
"persist.device_config.configuration.disable_rescue_party";
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
"persist.device_config.configuration.disable_rescue_party_factory_reset";
+ private static final String PROP_THROTTLE_DURATION_MIN_FLAG =
+ "persist.device_config.configuration.rescue_party_throttle_duration_min";
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
@@ -489,13 +491,14 @@ public class RescueParty {
switch(rescueLevel) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
- return PackageHealthObserverImpact.USER_IMPACT_LOW;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
case LEVEL_WARM_REBOOT:
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50;
case LEVEL_FACTORY_RESET:
- return PackageHealthObserverImpact.USER_IMPACT_HIGH;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_100;
default:
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
}
@@ -633,7 +636,7 @@ public class RescueParty {
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
mayPerformReboot(failedPackage)));
} else {
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
}
@@ -677,7 +680,7 @@ public class RescueParty {
@Override
public int onBootLoop(int mitigationCount) {
if (isDisabled()) {
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
}
@@ -721,7 +724,9 @@ public class RescueParty {
private boolean shouldThrottleReboot() {
Long lastResetTime = SystemProperties.getLong(PROP_LAST_FACTORY_RESET_TIME_MS, 0);
long now = System.currentTimeMillis();
- return now < lastResetTime + FACTORY_RESET_THROTTLE_DURATION_MS;
+ long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG,
+ DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN);
+ return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
}
private boolean isPersistentSystemApp(@NonNull String packageName) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index ae5dbe11495a..44e198b53761 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -213,7 +213,7 @@ final class ActivityManagerConstants extends ContentObserver {
private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
- private static final boolean DEFAULT_USE_MODERN_TRIM = false;
+ private static final boolean DEFAULT_USE_MODERN_TRIM = true;
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4a6c9b257b9a..b32f8c973e6a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8736,7 +8736,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
// debuggerd will terminate the process, but there's a backup where ActivityManager will
// also kill it. Avoid that.
- if (!recoverable) {
+ if (recoverable) {
+ mAppErrors.sendRecoverableCrashToAppExitInfo(r, crashInfo);
+ } else {
mAppErrors.crashApplication(r, crashInfo);
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index c343ec24412a..061bcd740f6b 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -555,6 +555,15 @@ class AppErrors {
}
}
+ void sendRecoverableCrashToAppExitInfo(
+ ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+ if (r == null || crashInfo == null
+ || !"Native crash".equals(crashInfo.exceptionClassName)) return;
+ synchronized (mService) {
+ mService.mProcessList.noteAppRecoverableCrash(r);
+ }
+ }
+
/**
* Bring up the "unexpected error" dialog box for a crashing app.
* Deal with edge cases (intercepts from instrumented applications,
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 44436369fb31..4c0dd115a3cc 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -308,6 +308,16 @@ public final class AppExitInfoTracker {
mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget();
}
+ void scheduleNoteAppRecoverableCrash(final ProcessRecord app) {
+ if (!mAppExitInfoLoaded.get() || app == null || app.info == null) return;
+
+ ApplicationExitInfo raw = obtainRawRecord(app, System.currentTimeMillis());
+ raw.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE);
+ raw.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN);
+ raw.setDescription("recoverable_crash");
+ mKillHandler.obtainMessage(KillHandler.MSG_APP_RECOVERABLE_CRASH, raw).sendToTarget();
+ }
+
void scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason,
final @SubReason int subReason, final String msg) {
if (!mAppExitInfoLoaded.get()) {
@@ -421,8 +431,24 @@ public final class AppExitInfoTracker {
scheduleLogToStatsdLocked(info, true);
}
+ /**
+ * Make note when ActivityManagerService gets a recoverable native crash, as the process isn't
+ * being killed but the crash should still be added to AppExitInfo. Also, because we're not
+ * crashing, don't log out to statsd.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void handleNoteAppRecoverableCrashLocked(final ApplicationExitInfo raw) {
+ addExitInfoLocked(raw, /* recoverable */ true);
+ }
+
@GuardedBy("mLock")
private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) {
+ return addExitInfoLocked(raw, /* recoverable */ false);
+ }
+
+ @GuardedBy("mLock")
+ private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw, boolean recoverable) {
if (!mAppExitInfoLoaded.get()) {
Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage");
return null;
@@ -438,7 +464,7 @@ public final class AppExitInfoTracker {
}
}
for (int i = 0; i < packages.length; i++) {
- addExitInfoInnerLocked(packages[i], uid, info);
+ addExitInfoInnerLocked(packages[i], uid, info, recoverable);
}
schedulePersistProcessExitInfo(false);
@@ -845,7 +871,8 @@ public final class AppExitInfoTracker {
}
@GuardedBy("mLock")
- private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info) {
+ private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info,
+ boolean recoverable) {
AppExitInfoContainer container = mData.get(packageName, uid);
if (container == null) {
container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
@@ -859,7 +886,11 @@ public final class AppExitInfoTracker {
}
mData.put(packageName, uid, container);
}
- container.addExitInfoLocked(info);
+ if (recoverable) {
+ container.addRecoverableCrashLocked(info);
+ } else {
+ container.addExitInfoLocked(info);
+ }
}
@GuardedBy("mLock")
@@ -1284,38 +1315,40 @@ public final class AppExitInfoTracker {
* A container class of {@link android.app.ApplicationExitInfo}
*/
final class AppExitInfoContainer {
- private SparseArray<ApplicationExitInfo> mInfos; // index is pid
+ private SparseArray<ApplicationExitInfo> mInfos; // index is a pid
+ private SparseArray<ApplicationExitInfo> mRecoverableCrashes; // index is a pid
private int mMaxCapacity;
private int mUid; // Application uid, not isolated uid.
AppExitInfoContainer(final int maxCapacity) {
mInfos = new SparseArray<ApplicationExitInfo>();
+ mRecoverableCrashes = new SparseArray<ApplicationExitInfo>();
mMaxCapacity = maxCapacity;
}
@GuardedBy("mLock")
- void getExitInfoLocked(final int filterPid, final int maxNum,
- ArrayList<ApplicationExitInfo> results) {
+ void getInfosLocked(SparseArray<ApplicationExitInfo> map, final int filterPid,
+ final int maxNum, ArrayList<ApplicationExitInfo> results) {
if (filterPid > 0) {
- ApplicationExitInfo r = mInfos.get(filterPid);
+ ApplicationExitInfo r = map.get(filterPid);
if (r != null) {
results.add(r);
}
} else {
- final int numRep = mInfos.size();
+ final int numRep = map.size();
if (maxNum <= 0 || numRep <= maxNum) {
// Return all records.
for (int i = 0; i < numRep; i++) {
- results.add(mInfos.valueAt(i));
+ results.add(map.valueAt(i));
}
Collections.sort(results,
(a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
} else {
if (maxNum == 1) {
// Most of the caller might be only interested with the most recent one
- ApplicationExitInfo r = mInfos.valueAt(0);
+ ApplicationExitInfo r = map.valueAt(0);
for (int i = 1; i < numRep; i++) {
- ApplicationExitInfo t = mInfos.valueAt(i);
+ ApplicationExitInfo t = map.valueAt(i);
if (r.getTimestamp() < t.getTimestamp()) {
r = t;
}
@@ -1326,7 +1359,7 @@ public final class AppExitInfoTracker {
ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
list.clear();
for (int i = 0; i < numRep; i++) {
- list.add(mInfos.valueAt(i));
+ list.add(map.valueAt(i));
}
Collections.sort(list,
(a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
@@ -1340,24 +1373,30 @@ public final class AppExitInfoTracker {
}
@GuardedBy("mLock")
- void addExitInfoLocked(ApplicationExitInfo info) {
+ void getExitInfoLocked(final int filterPid, final int maxNum,
+ ArrayList<ApplicationExitInfo> results) {
+ getInfosLocked(mInfos, filterPid, maxNum, results);
+ }
+
+ @GuardedBy("mLock")
+ void addInfoLocked(SparseArray<ApplicationExitInfo> map, ApplicationExitInfo info) {
int size;
- if ((size = mInfos.size()) >= mMaxCapacity) {
+ if ((size = map.size()) >= mMaxCapacity) {
int oldestIndex = -1;
long oldestTimeStamp = Long.MAX_VALUE;
for (int i = 0; i < size; i++) {
- ApplicationExitInfo r = mInfos.valueAt(i);
+ ApplicationExitInfo r = map.valueAt(i);
if (r.getTimestamp() < oldestTimeStamp) {
oldestTimeStamp = r.getTimestamp();
oldestIndex = i;
}
}
if (oldestIndex >= 0) {
- final File traceFile = mInfos.valueAt(oldestIndex).getTraceFile();
+ final File traceFile = map.valueAt(oldestIndex).getTraceFile();
if (traceFile != null) {
traceFile.delete();
}
- mInfos.removeAt(oldestIndex);
+ map.removeAt(oldestIndex);
}
}
// Claim the state information if there is any
@@ -1367,7 +1406,17 @@ public final class AppExitInfoTracker {
mActiveAppStateSummary, uid, pid));
info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid));
info.setAppTraceRetriever(mAppTraceRetriever);
- mInfos.append(pid, info);
+ map.append(pid, info);
+ }
+
+ @GuardedBy("mLock")
+ void addExitInfoLocked(ApplicationExitInfo info) {
+ addInfoLocked(mInfos, info);
+ }
+
+ @GuardedBy("mLock")
+ void addRecoverableCrashLocked(ApplicationExitInfo info) {
+ addInfoLocked(mRecoverableCrashes, info);
}
@GuardedBy("mLock")
@@ -1382,9 +1431,9 @@ public final class AppExitInfoTracker {
}
@GuardedBy("mLock")
- void destroyLocked() {
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- ApplicationExitInfo ai = mInfos.valueAt(i);
+ void destroyLocked(SparseArray<ApplicationExitInfo> map) {
+ for (int i = map.size() - 1; i >= 0; i--) {
+ ApplicationExitInfo ai = map.valueAt(i);
final File traceFile = ai.getTraceFile();
if (traceFile != null) {
traceFile.delete();
@@ -1395,24 +1444,37 @@ public final class AppExitInfoTracker {
}
@GuardedBy("mLock")
+ void destroyLocked() {
+ destroyLocked(mInfos);
+ destroyLocked(mRecoverableCrashes);
+ }
+
+ @GuardedBy("mLock")
void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) {
- if (callback != null) {
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
- case FOREACH_ACTION_REMOVE_ITEM:
- final File traceFile = mInfos.valueAt(i).getTraceFile();
- if (traceFile != null) {
- traceFile.delete();
- }
- mInfos.removeAt(i);
- break;
- case FOREACH_ACTION_STOP_ITERATION:
- i = 0;
- break;
- case FOREACH_ACTION_NONE:
- default:
- break;
- }
+ if (callback == null) return;
+ for (int i = mInfos.size() - 1; i >= 0; i--) {
+ switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
+ case FOREACH_ACTION_STOP_ITERATION: return;
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final File traceFile = mInfos.valueAt(i).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mInfos.removeAt(i);
+ break;
+ }
+ }
+ for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
+ switch (callback.apply(
+ mRecoverableCrashes.keyAt(i), mRecoverableCrashes.valueAt(i))) {
+ case FOREACH_ACTION_STOP_ITERATION: return;
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final File traceFile = mRecoverableCrashes.valueAt(i).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mRecoverableCrashes.removeAt(i);
+ break;
}
}
}
@@ -1423,6 +1485,9 @@ public final class AppExitInfoTracker {
for (int i = mInfos.size() - 1; i >= 0; i--) {
list.add(mInfos.valueAt(i));
}
+ for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
+ list.add(mRecoverableCrashes.valueAt(i));
+ }
Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
int size = list.size();
for (int i = 0; i < size; i++) {
@@ -1434,10 +1499,13 @@ public final class AppExitInfoTracker {
void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(AppsExitInfoProto.Package.User.UID, mUid);
- int size = mInfos.size();
- for (int i = 0; i < size; i++) {
+ for (int i = 0; i < mInfos.size(); i++) {
mInfos.valueAt(i).writeToProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
}
+ for (int i = 0; i < mRecoverableCrashes.size(); i++) {
+ mRecoverableCrashes.valueAt(i).writeToProto(
+ proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH);
+ }
proto.end(token);
}
@@ -1448,14 +1516,23 @@ public final class AppExitInfoTracker {
next != ProtoInputStream.NO_MORE_FIELDS;
next = proto.nextField()) {
switch (next) {
- case (int) AppsExitInfoProto.Package.User.UID:
+ case (int) AppsExitInfoProto.Package.User.UID: {
mUid = proto.readInt(AppsExitInfoProto.Package.User.UID);
break;
- case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO:
+ }
+ case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO: {
ApplicationExitInfo info = new ApplicationExitInfo();
info.readFromProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
mInfos.put(info.getPid(), info);
break;
+ }
+ case (int) AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH: {
+ ApplicationExitInfo info = new ApplicationExitInfo();
+ info.readFromProto(
+ proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH);
+ mRecoverableCrashes.put(info.getPid(), info);
+ break;
+ }
}
}
proto.end(token);
@@ -1472,6 +1549,11 @@ public final class AppExitInfoTracker {
list.add(mInfos.valueAt(i));
}
}
+ for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
+ if (filterPid == 0 || filterPid == mRecoverableCrashes.keyAt(i)) {
+ list.add(mRecoverableCrashes.valueAt(i));
+ }
+ }
return list;
}
}
@@ -1610,6 +1692,7 @@ public final class AppExitInfoTracker {
static final int MSG_PROC_DIED = 4103;
static final int MSG_APP_KILL = 4104;
static final int MSG_STATSD_LOG = 4105;
+ static final int MSG_APP_RECOVERABLE_CRASH = 4106;
KillHandler(Looper looper) {
super(looper, null, true);
@@ -1648,6 +1731,14 @@ public final class AppExitInfoTracker {
}
}
break;
+ case MSG_APP_RECOVERABLE_CRASH: {
+ ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
+ synchronized (mLock) {
+ handleNoteAppRecoverableCrashLocked(raw);
+ }
+ recycleRawRecord(raw);
+ }
+ break;
default:
super.handleMessage(msg);
}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index bfc8251d97bb..0767218ec065 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -18,9 +18,9 @@ package com.android.server.am;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
-import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
+import android.annotation.CheckResult;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -188,6 +188,12 @@ class BroadcastProcessQueue {
private @Reason int mRunnableAtReason = REASON_EMPTY;
private boolean mRunnableAtInvalidated;
+ /**
+ * Last state applied by {@link #updateDeferredStates}, used to quickly
+ * determine if a state transition is occurring.
+ */
+ private boolean mLastDeferredStates;
+
private boolean mUidCached;
private boolean mProcessInstrumented;
private boolean mProcessPersistent;
@@ -236,7 +242,15 @@ class BroadcastProcessQueue {
*/
@Nullable
public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
- int recordIndex, boolean wouldBeSkipped) {
+ int recordIndex, boolean wouldBeSkipped,
+ @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
+ // When updateDeferredStates() has already applied a deferred state to
+ // all pending items, apply to this new broadcast too
+ if (mLastDeferredStates && record.deferUntilActive
+ && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
+ deferredStatesApplyConsumer.accept(record, recordIndex);
+ }
+
if (record.isReplacePending()) {
final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex,
wouldBeSkipped);
@@ -341,7 +355,12 @@ class BroadcastProcessQueue {
* Predicates that choose to remove a broadcast <em>must</em> finish
* delivery of the matched broadcast, to ensure that situations like ordered
* broadcasts are handled consistently.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
*/
+ @CheckResult
public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate,
@NonNull BroadcastConsumer consumer, boolean andRemove) {
boolean didSomething = false;
@@ -354,6 +373,7 @@ class BroadcastProcessQueue {
return didSomething;
}
+ @CheckResult
private boolean forEachMatchingBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
@NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer,
boolean andRemove) {
@@ -370,6 +390,10 @@ class BroadcastProcessQueue {
args.recycle();
it.remove();
onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped);
+ } else {
+ // Even if we're leaving broadcast in queue, it may have
+ // been mutated in such a way to change our runnable time
+ invalidateRunnableAt();
}
didSomething = true;
}
@@ -381,32 +405,44 @@ class BroadcastProcessQueue {
/**
* Update the actively running "warm" process for this process.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
*/
- public void setProcessAndUidCached(@Nullable ProcessRecord app, boolean uidCached) {
+ @CheckResult
+ public boolean setProcessAndUidCached(@Nullable ProcessRecord app, boolean uidCached) {
this.app = app;
- if (app != null) {
- setUidCached(uidCached);
- setProcessInstrumented(app.getActiveInstrumentation() != null);
- setProcessPersistent(app.isPersistent());
- } else {
- setUidCached(uidCached);
- setProcessInstrumented(false);
- setProcessPersistent(false);
- }
// Since we may have just changed our PID, invalidate cached strings
mCachedToString = null;
mCachedToShortString = null;
+
+ boolean didSomething = false;
+ if (app != null) {
+ didSomething |= setUidCached(uidCached);
+ didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null);
+ didSomething |= setProcessPersistent(app.isPersistent());
+ } else {
+ didSomething |= setUidCached(uidCached);
+ didSomething |= setProcessInstrumented(false);
+ didSomething |= setProcessPersistent(false);
+ }
+ return didSomething;
}
/**
* Update if this process is in the "cached" state, typically signaling that
* broadcast dispatch should be paused or delayed.
*/
- private void setUidCached(boolean uidCached) {
+ @CheckResult
+ private boolean setUidCached(boolean uidCached) {
if (mUidCached != uidCached) {
mUidCached = uidCached;
invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
}
}
@@ -415,10 +451,14 @@ class BroadcastProcessQueue {
* signaling that broadcast dispatch should bypass all pauses or delays, to
* avoid holding up test suites.
*/
- private void setProcessInstrumented(boolean instrumented) {
+ @CheckResult
+ private boolean setProcessInstrumented(boolean instrumented) {
if (mProcessInstrumented != instrumented) {
mProcessInstrumented = instrumented;
invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
}
}
@@ -426,10 +466,14 @@ class BroadcastProcessQueue {
* Update if this process is in the "persistent" state, which signals broadcast dispatch should
* bypass all pauses or delays to prevent the system from becoming out of sync with itself.
*/
- private void setProcessPersistent(boolean persistent) {
+ @CheckResult
+ private boolean setProcessPersistent(boolean persistent) {
if (mProcessPersistent != persistent) {
mProcessPersistent = persistent;
invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
}
}
@@ -649,8 +693,20 @@ class BroadcastProcessQueue {
return mActive != null;
}
- void forceDelayBroadcastDelivery(long delayedDurationMs) {
- mForcedDelayedDurationMs = delayedDurationMs;
+ /**
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
+ */
+ @CheckResult
+ boolean forceDelayBroadcastDelivery(long delayedDurationMs) {
+ if (mForcedDelayedDurationMs != delayedDurationMs) {
+ mForcedDelayedDurationMs = delayedDurationMs;
+ invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -709,7 +765,7 @@ class BroadcastProcessQueue {
|| consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit);
final boolean isLPQueueEligible = shouldConsiderLPQueue
&& nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime
- && !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex);
+ && !nextLPRecord.isBlocked(nextLPRecordIndex);
return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue;
}
@@ -722,10 +778,21 @@ class BroadcastProcessQueue {
* broadcasts would be prioritized for dispatching, even if there are urgent broadcasts
* waiting. This is typically used in case there are callers waiting for "barrier" to be
* reached.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
*/
+ @CheckResult
@VisibleForTesting
- void setPrioritizeEarliest(boolean prioritizeEarliest) {
- mPrioritizeEarliest = prioritizeEarliest;
+ boolean setPrioritizeEarliest(boolean prioritizeEarliest) {
+ if (mPrioritizeEarliest != prioritizeEarliest) {
+ mPrioritizeEarliest = prioritizeEarliest;
+ invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -912,39 +979,20 @@ class BroadcastProcessQueue {
}
}
- private boolean blockedOnOrderedDispatch(BroadcastRecord r, int index) {
- final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index];
-
- int existingDeferredCount = 0;
- if (r.deferUntilActive) {
- for (int i = 0; i < index; i++) {
- if (r.deferredUntilActive[i]) existingDeferredCount++;
- }
- }
-
- // We might be blocked waiting for other receivers to finish,
- // typically for an ordered broadcast or priority traunches
- if ((r.terminalCount + existingDeferredCount) < blockedUntilTerminalCount
- && !isDeliveryStateTerminal(r.getDeliveryState(index))) {
- return true;
- }
- return false;
- }
-
/**
- * Update {@link #getRunnableAt()} if it's currently invalidated.
+ * Update {@link #getRunnableAt()}, when needed.
*/
- private void updateRunnableAt() {
- final SomeArgs next = peekNextBroadcast();
+ void updateRunnableAt() {
+ if (!mRunnableAtInvalidated) return;
mRunnableAtInvalidated = false;
+
+ final SomeArgs next = peekNextBroadcast();
if (next != null) {
final BroadcastRecord r = (BroadcastRecord) next.arg1;
final int index = next.argi1;
final long runnableAt = r.enqueueTime;
- // If we're specifically queued behind other ordered dispatch activity,
- // we aren't runnable yet
- if (blockedOnOrderedDispatch(r, index)) {
+ if (r.isBlocked(index)) {
mRunnableAt = Long.MAX_VALUE;
mRunnableAtReason = REASON_BLOCKED;
return;
@@ -1047,10 +1095,44 @@ class BroadcastProcessQueue {
}
/**
+ * Update {@link BroadcastRecord.DELIVERY_DEFERRED} states of all our
+ * pending broadcasts, when needed.
+ */
+ void updateDeferredStates(@NonNull BroadcastConsumer applyConsumer,
+ @NonNull BroadcastConsumer clearConsumer) {
+ // When all we have pending is deferred broadcasts, and we're cached,
+ // then we want everything to be marked deferred
+ final boolean wantDeferredStates = (mCountDeferred > 0)
+ && (mCountDeferred == mCountEnqueued) && mUidCached;
+
+ if (mLastDeferredStates != wantDeferredStates) {
+ mLastDeferredStates = wantDeferredStates;
+ if (wantDeferredStates) {
+ forEachMatchingBroadcast((r, i) -> {
+ return r.deferUntilActive
+ && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
+ }, applyConsumer, false);
+ } else {
+ forEachMatchingBroadcast((r, i) -> {
+ return r.deferUntilActive
+ && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ }, clearConsumer, false);
+ }
+ }
+ }
+
+ /**
* Check overall health, confirming things are in a reasonable state and
* that we're not wedged.
*/
public void assertHealthLocked() {
+ // If we're not actively running, we should be sorted into the runnable
+ // list, and if we're invalidated then someone likely forgot to invoke
+ // updateRunnableList() to re-sort us into place
+ if (!isActive()) {
+ checkState(!mRunnableAtInvalidated, "mRunnableAtInvalidated");
+ }
+
assertHealthLocked(mPending);
assertHealthLocked(mPendingUrgent);
assertHealthLocked(mPendingOffload);
@@ -1153,19 +1235,30 @@ class BroadcastProcessQueue {
return mCachedToShortString;
}
+ public String describeStateLocked() {
+ return describeStateLocked(SystemClock.uptimeMillis());
+ }
+
+ public String describeStateLocked(@UptimeMillisLong long now) {
+ final StringBuilder sb = new StringBuilder();
+ if (isRunnable()) {
+ sb.append("runnable at ");
+ TimeUtils.formatDuration(getRunnableAt(), now, sb);
+ } else {
+ sb.append("not runnable");
+ }
+ sb.append(" because ");
+ sb.append(reasonToString(mRunnableAtReason));
+ return sb.toString();
+ }
+
@NeverCompile
public void dumpLocked(@UptimeMillisLong long now, @NonNull IndentingPrintWriter pw) {
if ((mActive == null) && isEmpty()) return;
pw.print(toShortString());
- if (isRunnable()) {
- pw.print(" runnable at ");
- TimeUtils.formatDuration(getRunnableAt(), now, pw);
- } else {
- pw.print(" not runnable");
- }
- pw.print(" because ");
- pw.print(reasonToString(mRunnableAtReason));
+ pw.print(" ");
+ pw.print(describeStateLocked(now));
pw.println();
pw.increaseIndent();
@@ -1262,12 +1355,12 @@ class BroadcastProcessQueue {
pw.print(info.activityInfo.name);
}
pw.println();
- final int blockedUntilTerminalCount = record.blockedUntilTerminalCount[recordIndex];
- if (blockedUntilTerminalCount != -1) {
+ final int blockedUntilBeyondCount = record.blockedUntilBeyondCount[recordIndex];
+ if (blockedUntilBeyondCount != -1) {
pw.print(" blocked until ");
- pw.print(blockedUntilTerminalCount);
+ pw.print(blockedUntilBeyondCount);
pw.print(", currently at ");
- pw.print(record.terminalCount);
+ pw.print(record.beyondCount);
pw.print(" of ");
pw.println(record.receivers.size());
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index c2bd84f7e665..8735f8a37b8b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -327,6 +327,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
return;
}
+ // To place ourselves correctly in the runnable list, we may need to
+ // update internals that may have been invalidated; we wait until now at
+ // the last possible moment to avoid duplicated work
+ queue.updateDeferredStates(mBroadcastConsumerDeferApply, mBroadcastConsumerDeferClear);
+ queue.updateRunnableAt();
+
final boolean wantQueue = queue.isRunnable();
final boolean inQueue = (queue == mRunnableHead) || (queue.runnableAtPrev != null)
|| (queue.runnableAtNext != null);
@@ -352,8 +358,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// If app isn't running, and there's nothing in the queue, clean up
if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
removeProcessQueue(queue.processName, queue.uid);
- } else {
- updateQueueDeferred(queue);
}
}
@@ -619,14 +623,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
enqueuedBroadcast = true;
final BroadcastRecord replacedBroadcast = queue.enqueueOrReplaceBroadcast(
- r, i, wouldBeSkipped);
+ r, i, wouldBeSkipped, mBroadcastConsumerDeferApply);
if (replacedBroadcast != null) {
replacedBroadcasts.add(replacedBroadcast);
}
- if (r.isDeferUntilActive() && queue.isDeferredUntilActive()) {
- setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED,
- "deferred at enqueue time");
- }
updateRunnableList(queue);
enqueueUpdateRunningList();
}
@@ -1008,6 +1008,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
final BroadcastRecord r = queue.getActive();
+ final int index = queue.getActiveIndex();
if (r.ordered) {
r.resultCode = resultCode;
r.resultData = resultData;
@@ -1015,18 +1016,24 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (!r.isNoAbort()) {
r.resultAbort = resultAbort;
}
+ }
- // When the caller aborted an ordered broadcast, we mark all
- // remaining receivers as skipped
- if (r.resultAbort) {
- for (int i = r.terminalCount + 1; i < r.receivers.size(); i++) {
- setDeliveryState(null, null, r, i, r.receivers.get(i),
- BroadcastRecord.DELIVERY_SKIPPED, "resultAbort");
- }
+ // To ensure that "beyond" high-water marks are updated in a monotonic
+ // way, we finish this receiver before possibly skipping any remaining
+ // aborted receivers
+ final boolean res = finishReceiverActiveLocked(queue,
+ BroadcastRecord.DELIVERY_DELIVERED, "remote app");
+
+ // When the caller aborted an ordered broadcast, we mark all
+ // remaining receivers as skipped
+ if (r.resultAbort) {
+ for (int i = index + 1; i < r.receivers.size(); i++) {
+ setDeliveryState(null, null, r, i, r.receivers.get(i),
+ BroadcastRecord.DELIVERY_SKIPPED, "resultAbort");
}
}
- return finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED, "remote app");
+ return res;
}
/**
@@ -1108,21 +1115,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
@NonNull Object receiver, @DeliveryState int newDeliveryState,
@NonNull String reason) {
final int cookie = traceBegin("setDeliveryState");
+
+ // Remember the old state and apply the new state
final int oldDeliveryState = getDeliveryState(r, index);
- boolean checkFinished = false;
-
- // Only apply state when we haven't already reached a terminal state;
- // this is how we ignore racing timeout messages
- if (!isDeliveryStateTerminal(oldDeliveryState)) {
- r.setDeliveryState(index, newDeliveryState, reason);
- if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
- r.deferredCount--;
- } else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
- // If we're deferring a broadcast, maybe that's enough to unblock the final callback
- r.deferredCount++;
- checkFinished = true;
- }
- }
+ final boolean beyondCountChanged = r.setDeliveryState(index, newDeliveryState, reason);
// Emit any relevant tracing results when we're changing the delivery
// state as part of running from a queue
@@ -1147,15 +1143,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
+ deliveryStateToString(newDeliveryState) + " because " + reason);
}
- r.terminalCount++;
notifyFinishReceiver(queue, app, r, index, receiver);
- checkFinished = true;
}
- // When entire ordered broadcast finished, deliver final result
- if (checkFinished) {
- final boolean recordFinished =
- ((r.terminalCount + r.deferredCount) == r.receivers.size());
- if (recordFinished) {
+
+ // When we've reached a new high-water mark, we might be in a position
+ // to unblock other receivers or the final resultTo
+ if (beyondCountChanged) {
+ if (r.beyondCount == r.receivers.size()) {
scheduleResultTo(r);
}
@@ -1255,14 +1249,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
r.resultExtras = null;
};
- private final BroadcastConsumer mBroadcastConsumerDefer = (r, i) -> {
+ private final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
- "mBroadcastConsumerDefer");
+ "mBroadcastConsumerDeferApply");
};
- private final BroadcastConsumer mBroadcastConsumerUndoDefer = (r, i) -> {
+ private final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
- "mBroadcastConsumerUndoDefer");
+ "mBroadcastConsumerDeferClear");
};
/**
@@ -1278,7 +1272,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
final long now = SystemClock.uptimeMillis();
if (now > mLastTestFailureTime + DateUtils.SECOND_IN_MILLIS) {
mLastTestFailureTime = now;
- pw.println("Test " + label + " failed due to " + leaf.toShortString());
+ pw.println("Test " + label + " failed due to " + leaf.toShortString() + " "
+ + leaf.describeStateLocked());
pw.flush();
}
return false;
@@ -1315,34 +1310,25 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
return didSomething;
}
- private void forEachMatchingQueue(
+ private boolean forEachMatchingQueue(
@NonNull Predicate<BroadcastProcessQueue> queuePredicate,
@NonNull Consumer<BroadcastProcessQueue> queueConsumer) {
+ boolean didSomething = false;
for (int i = mProcessQueues.size() - 1; i >= 0; i--) {
BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
while (leaf != null) {
if (queuePredicate.test(leaf)) {
queueConsumer.accept(leaf);
updateRunnableList(leaf);
+ didSomething = true;
}
leaf = leaf.processNameNext;
}
}
- }
-
- private void updateQueueDeferred(
- @NonNull BroadcastProcessQueue leaf) {
- if (leaf.isDeferredUntilActive()) {
- leaf.forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive && (r.getDeliveryState(i)
- == BroadcastRecord.DELIVERY_PENDING);
- }, mBroadcastConsumerDefer, false);
- } else if (leaf.hasDeferredBroadcasts()) {
- leaf.forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive && (r.getDeliveryState(i)
- == BroadcastRecord.DELIVERY_DEFERRED);
- }, mBroadcastConsumerUndoDefer, false);
+ if (didSomething) {
+ enqueueUpdateRunningList();
}
+ return didSomething;
}
@Override
@@ -1365,8 +1351,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// Update internal state by refreshing values previously
// read from any known running process
setQueueProcess(leaf, leaf.app);
- updateQueueDeferred(leaf);
- updateRunnableList(leaf);
leaf = leaf.processNameNext;
}
enqueueUpdateRunningList();
@@ -1535,19 +1519,31 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
+ @SuppressWarnings("CheckResult")
private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) {
if (!queue.isProcessWarm()) {
- setQueueProcess(queue, mService.getProcessRecordLocked(queue.processName, queue.uid));
+ // This is a bit awkward; we're in the middle of traversing the
+ // runnable queue, so we can't reorder that list if the runnable
+ // time changes here. However, if this process was just found to be
+ // warm via this operation, we're going to immediately promote it to
+ // be running, and any side effect of this operation will then apply
+ // after it's finished and is returned to the runnable list.
+ queue.setProcessAndUidCached(
+ mService.getProcessRecordLocked(queue.processName, queue.uid),
+ mUidCached.get(queue.uid, false));
}
}
/**
* Update the {@link ProcessRecord} associated with the given
- * {@link BroadcastProcessQueue}.
+ * {@link BroadcastProcessQueue}. Also updates any runnable status that
+ * might have changed as a side-effect.
*/
private void setQueueProcess(@NonNull BroadcastProcessQueue queue,
@Nullable ProcessRecord app) {
- queue.setProcessAndUidCached(app, mUidCached.get(queue.uid, false));
+ if (queue.setProcessAndUidCached(app, mUidCached.get(queue.uid, false))) {
+ updateRunnableList(queue);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index c368290386a0..64fe39314f0e 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -24,6 +24,7 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+import android.annotation.CheckResult;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
@@ -101,8 +102,7 @@ final class BroadcastRecord extends Binder {
final @NonNull List<Object> receivers; // contains BroadcastFilter and ResolveInfo
final @DeliveryState int[] delivery; // delivery state of each receiver
final @NonNull String[] deliveryReasons; // reasons for delivery state of each receiver
- final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred
- final int[] blockedUntilTerminalCount; // blocked until count of each receiver
+ final int[] blockedUntilBeyondCount; // blocked until count of each receiver
@Nullable ProcessRecord resultToApp; // who receives final result if non-null
@Nullable IIntentReceiver resultTo; // who receives final result if non-null
boolean deferred;
@@ -134,6 +134,7 @@ final class BroadcastRecord extends Binder {
int manifestSkipCount; // number of manifest receivers skipped.
int terminalCount; // number of receivers in terminal state.
int deferredCount; // number of receivers in deferred state.
+ int beyondCount; // high-water number of receivers we've moved beyond.
@Nullable BroadcastQueue queue; // the outbound queue handling this broadcast
// Determines the privileges the app's process has in regard to background starts.
@@ -219,6 +220,23 @@ final class BroadcastRecord extends Binder {
}
}
+ /**
+ * Return if the given delivery state is "beyond", which means that we've
+ * moved beyond this receiver, and future receivers are now unblocked.
+ */
+ static boolean isDeliveryStateBeyond(@DeliveryState int deliveryState) {
+ switch (deliveryState) {
+ case DELIVERY_DELIVERED:
+ case DELIVERY_SKIPPED:
+ case DELIVERY_TIMEOUT:
+ case DELIVERY_FAILURE:
+ case DELIVERY_DEFERRED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
ProcessRecord curApp; // hosting application of current receiver.
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // the manifest receiver that is currently running.
@@ -356,7 +374,7 @@ final class BroadcastRecord extends Binder {
TimeUtils.formatDuration(terminalTime[i] - scheduledTime[i], pw);
pw.print(' ');
}
- pw.print("("); pw.print(blockedUntilTerminalCount[i]); pw.print(") ");
+ pw.print("("); pw.print(blockedUntilBeyondCount[i]); pw.print(") ");
pw.print("#"); pw.print(i); pw.print(": ");
if (o instanceof BroadcastFilter) {
pw.println(o);
@@ -411,8 +429,7 @@ final class BroadcastRecord extends Binder {
urgent = calculateUrgent(_intent, _options);
deferUntilActive = calculateDeferUntilActive(_callingUid,
_options, _resultTo, _serialized, urgent);
- deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0];
- blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
+ blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized);
scheduledTime = new long[delivery.length];
terminalTime = new long[delivery.length];
resultToApp = _resultToApp;
@@ -423,7 +440,7 @@ final class BroadcastRecord extends Binder {
ordered = _serialized;
sticky = _sticky;
initialSticky = _initialSticky;
- prioritized = isPrioritized(blockedUntilTerminalCount, _serialized);
+ prioritized = isPrioritized(blockedUntilBeyondCount, _serialized);
userId = _userId;
nextReceiver = 0;
state = IDLE;
@@ -467,8 +484,7 @@ final class BroadcastRecord extends Binder {
delivery = from.delivery;
deliveryReasons = from.deliveryReasons;
deferUntilActive = from.deferUntilActive;
- deferredUntilActive = from.deferredUntilActive;
- blockedUntilTerminalCount = from.blockedUntilTerminalCount;
+ blockedUntilBeyondCount = from.blockedUntilBeyondCount;
scheduledTime = from.scheduledTime;
terminalTime = from.terminalTime;
resultToApp = from.resultToApp;
@@ -627,32 +643,72 @@ final class BroadcastRecord extends Binder {
/**
* Update the delivery state of the given {@link #receivers} index.
* Automatically updates any time measurements related to state changes.
+ *
+ * @return if {@link #beyondCount} changed due to this state transition,
+ * indicating that other events may be unblocked.
*/
- void setDeliveryState(int index, @DeliveryState int deliveryState,
+ @CheckResult
+ boolean setDeliveryState(int index, @DeliveryState int newDeliveryState,
@NonNull String reason) {
- delivery[index] = deliveryState;
- deliveryReasons[index] = reason;
- if (deferUntilActive) deferredUntilActive[index] = false;
- switch (deliveryState) {
- case DELIVERY_DELIVERED:
- case DELIVERY_SKIPPED:
- case DELIVERY_TIMEOUT:
- case DELIVERY_FAILURE:
- terminalTime[index] = SystemClock.uptimeMillis();
+ final int oldDeliveryState = delivery[index];
+ if (isDeliveryStateTerminal(oldDeliveryState)
+ || newDeliveryState == oldDeliveryState) {
+ // We've already arrived in terminal or requested state, so leave
+ // any statistics and reasons intact from the first transition
+ return false;
+ }
+
+ switch (oldDeliveryState) {
+ case DELIVERY_DEFERRED:
+ deferredCount--;
break;
+ }
+ switch (newDeliveryState) {
case DELIVERY_SCHEDULED:
scheduledTime[index] = SystemClock.uptimeMillis();
break;
case DELIVERY_DEFERRED:
- if (deferUntilActive) deferredUntilActive[index] = true;
+ deferredCount++;
+ break;
+ case DELIVERY_DELIVERED:
+ case DELIVERY_SKIPPED:
+ case DELIVERY_TIMEOUT:
+ case DELIVERY_FAILURE:
+ terminalTime[index] = SystemClock.uptimeMillis();
+ terminalCount++;
break;
}
+
+ delivery[index] = newDeliveryState;
+ deliveryReasons[index] = reason;
+
+ // If this state change might bring us to a new high-water mark, bring
+ // ourselves as high as we possibly can
+ final int oldBeyondCount = beyondCount;
+ if (index >= beyondCount) {
+ for (int i = beyondCount; i < delivery.length; i++) {
+ if (isDeliveryStateBeyond(getDeliveryState(i))) {
+ beyondCount = i + 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return (beyondCount != oldBeyondCount);
}
@DeliveryState int getDeliveryState(int index) {
return delivery[index];
}
+ /**
+ * @return if the given {@link #receivers} index should be considered
+ * blocked based on the current status of the overall broadcast.
+ */
+ boolean isBlocked(int index) {
+ return (beyondCount < blockedUntilBeyondCount[index]);
+ }
+
boolean wasDeliveryAttempted(int index) {
final int deliveryState = getDeliveryState(index);
switch (deliveryState) {
@@ -757,36 +813,36 @@ final class BroadcastRecord extends Binder {
* has prioritized tranches of receivers.
*/
@VisibleForTesting
- static boolean isPrioritized(@NonNull int[] blockedUntilTerminalCount,
+ static boolean isPrioritized(@NonNull int[] blockedUntilBeyondCount,
boolean ordered) {
- return !ordered && (blockedUntilTerminalCount.length > 0)
- && (blockedUntilTerminalCount[0] != -1);
+ return !ordered && (blockedUntilBeyondCount.length > 0)
+ && (blockedUntilBeyondCount[0] != -1);
}
/**
- * Calculate the {@link #terminalCount} that each receiver should be
+ * Calculate the {@link #beyondCount} that each receiver should be
* considered blocked until.
* <p>
* For example, in an ordered broadcast, receiver {@code N} is blocked until
- * receiver {@code N-1} reaches a terminal state. Similarly, in a
- * prioritized broadcast, receiver {@code N} is blocked until all receivers
- * of a higher priority reach a terminal state.
+ * receiver {@code N-1} reaches a terminal or deferred state. Similarly, in
+ * a prioritized broadcast, receiver {@code N} is blocked until all
+ * receivers of a higher priority reach a terminal or deferred state.
* <p>
- * When there are no terminal count constraints, the blocked value for each
+ * When there are no beyond count constraints, the blocked value for each
* receiver is {@code -1}.
*/
@VisibleForTesting
- static @NonNull int[] calculateBlockedUntilTerminalCount(
+ static @NonNull int[] calculateBlockedUntilBeyondCount(
@NonNull List<Object> receivers, boolean ordered) {
final int N = receivers.size();
- final int[] blockedUntilTerminalCount = new int[N];
+ final int[] blockedUntilBeyondCount = new int[N];
int lastPriority = 0;
int lastPriorityIndex = 0;
for (int i = 0; i < N; i++) {
if (ordered) {
// When sending an ordered broadcast, we need to block this
// receiver until all previous receivers have terminated
- blockedUntilTerminalCount[i] = i;
+ blockedUntilBeyondCount[i] = i;
} else {
// When sending a prioritized broadcast, we only need to wait
// for the previous tranche of receivers to be terminated
@@ -794,18 +850,18 @@ final class BroadcastRecord extends Binder {
if ((i == 0) || (thisPriority != lastPriority)) {
lastPriority = thisPriority;
lastPriorityIndex = i;
- blockedUntilTerminalCount[i] = i;
+ blockedUntilBeyondCount[i] = i;
} else {
- blockedUntilTerminalCount[i] = lastPriorityIndex;
+ blockedUntilBeyondCount[i] = lastPriorityIndex;
}
}
}
// If the entire list is in the same priority tranche, mark as -1 to
// indicate that none of them need to wait
- if (N > 0 && blockedUntilTerminalCount[N - 1] == 0) {
- Arrays.fill(blockedUntilTerminalCount, -1);
+ if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) {
+ Arrays.fill(blockedUntilBeyondCount, -1);
}
- return blockedUntilTerminalCount;
+ return blockedUntilBeyondCount;
}
static int getReceiverUid(@NonNull Object receiver) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f42087ff8006..8ad76cb668bf 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1326,8 +1326,7 @@ public final class CachedAppOptimizer {
UidRecord uidRec = app.getUidRecord();
if (uidRec != null && uidRec.isFrozen()) {
uidRec.setFrozen(false);
- mFreezeHandler.removeMessages(UID_FROZEN_STATE_CHANGED_MSG, app);
- reportOneUidFrozenStateChanged(app.uid, false);
+ postUidFrozenMessage(uidRec.getUid(), false);
}
opt.setFreezerOverride(false);
@@ -1468,8 +1467,7 @@ public final class CachedAppOptimizer {
UidRecord uidRec = app.getUidRecord();
if (uidRec != null && uidRec.isFrozen()) {
uidRec.setFrozen(false);
- mFreezeHandler.removeMessages(UID_FROZEN_STATE_CHANGED_MSG, app);
- reportOneUidFrozenStateChanged(app.uid, false);
+ postUidFrozenMessage(uidRec.getUid(), false);
}
mFrozenProcesses.delete(app.getPid());
@@ -1998,6 +1996,15 @@ public final class CachedAppOptimizer {
mAm.reportUidFrozenStateChanged(uids, frozenStates);
}
+ private void postUidFrozenMessage(int uid, boolean frozen) {
+ final Integer uidObj = Integer.valueOf(uid);
+ mFreezeHandler.removeEqualMessages(UID_FROZEN_STATE_CHANGED_MSG, uidObj);
+
+ final int op = frozen ? 1 : 0;
+ mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(UID_FROZEN_STATE_CHANGED_MSG, op,
+ 0, uidObj));
+ }
+
private final class FreezeHandler extends Handler implements
ProcLocksReader.ProcLocksReaderCallback {
private FreezeHandler() {
@@ -2028,7 +2035,9 @@ public final class CachedAppOptimizer {
reportUnfreeze(pid, frozenDuration, processName, reason);
break;
case UID_FROZEN_STATE_CHANGED_MSG:
- reportOneUidFrozenStateChanged(((ProcessRecord) msg.obj).uid, true);
+ final boolean frozen = (msg.arg1 == 1);
+ final int uid = (int) msg.obj;
+ reportOneUidFrozenStateChanged(uid, frozen);
break;
case DEADLOCK_WATCHDOG_MSG:
try {
@@ -2139,8 +2148,8 @@ public final class CachedAppOptimizer {
final UidRecord uidRec = proc.getUidRecord();
if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) {
uidRec.setFrozen(true);
- mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(
- UID_FROZEN_STATE_CHANGED_MSG, proc));
+
+ postUidFrozenMessage(uidRec.getUid(), true);
}
}
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 9ff2cd0649d4..727d4df96c47 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -22,7 +22,7 @@ import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.expresslog.Counter;
+import com.android.modules.expresslog.Counter;
/** Rate limiter for adding errors into dropbox. */
public class DropboxRateLimiter {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 70a696c72a9d..ca41f429a0de 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -51,7 +51,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.expresslog.Counter;
+import com.android.modules.expresslog.Counter;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.anr.AnrLatencyTracker;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a237a070c891..312f98ad6e09 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -5205,6 +5205,17 @@ public final class ProcessList {
}
/**
+ * Called by ActivityManagerService when a recoverable native crash occurs.
+ */
+ @GuardedBy("mService")
+ void noteAppRecoverableCrash(final ProcessRecord app) {
+ if (DEBUG_PROCESSES) {
+ Slog.i(TAG, "note: " + app + " has a recoverable native crash");
+ }
+ mAppExitInfoTracker.scheduleNoteAppRecoverableCrash(app);
+ }
+
+ /**
* Called by ActivityManagerService when it decides to kill an application process.
*/
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7aae4d5b0215..ffb40ee959a4 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -579,7 +579,9 @@ class ProcessRecord implements WindowProcessListener {
processName = _processName;
sdkSandboxClientAppPackage = _sdkSandboxClientAppPackage;
if (isSdkSandbox) {
- sdkSandboxClientAppVolumeUuid = getClientInfoForSdkSandbox().volumeUuid;
+ final ApplicationInfo clientInfo = getClientInfoForSdkSandbox();
+ sdkSandboxClientAppVolumeUuid = clientInfo != null
+ ? clientInfo.volumeUuid : null;
} else {
sdkSandboxClientAppVolumeUuid = null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 7ae31b2a114d..50d375c56f4a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -212,6 +212,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
// 1) Authenticated == true
// 2) Error occurred
// 3) Authenticated == false
+ // 4) onLockout
+ // 5) onLockoutTimed
mCallback.onClientFinished(this, true /* success */);
}
@@ -304,11 +306,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
PerformanceTracker.getInstanceForSensorId(getSensorId())
.incrementTimedLockoutForUser(getTargetUserId());
- try {
- getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
+ onError(error, 0 /* vendorCode */);
}
@Override
@@ -323,10 +321,6 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut
PerformanceTracker.getInstanceForSensorId(getSensorId())
.incrementPermanentLockoutForUser(getTargetUserId());
- try {
- getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
+ onError(error, 0 /* vendorCode */);
}
}
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 1a53fec82d98..c5037b7012f2 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
@@ -465,7 +465,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
BaseClientMonitor clientMonitor,
boolean success) {
mAuthSessionCoordinator.authEndedFor(userId, Utils.getCurrentStrength(sensorId),
- sensorId, requestId, success);
+ sensorId, requestId, client.wasAuthSuccessful());
}
});
});
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index 2ac283370886..c039a836843c 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -334,8 +334,8 @@ public class FontManagerShellCommand extends ShellCommand {
}
private int installCert(ShellCommand shell) throws SystemFontException {
- if (!(Build.IS_USERDEBUG || Build.IS_ENG)) {
- throw new SecurityException("Only userdebug/eng device can add debug certificate");
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException("Only debuggable device can add debug certificate");
}
if (Binder.getCallingUid() != Process.ROOT_UID) {
throw new SecurityException("Only root can add debug certificate");
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 653b71828c5b..5f783742bd26 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -519,17 +519,24 @@ public class ContextHubService extends IContextHubService.Stub {
BroadcastReceiver btReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())
- || BluetoothAdapter.ACTION_BLE_STATE_CHANGED.equals(
- intent.getAction())) {
+ if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
sendBtSettingUpdate(/* forceUpdate= */ false);
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
mContext.registerReceiver(btReceiver, filter);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE),
+ /* notifyForDescendants= */ false,
+ new ContentObserver(/* handler= */ null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ sendBtSettingUpdate(/* forceUpdate= */ false);
+ }
+ }, UserHandle.USER_ALL);
}
/**
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 273afcc9f769..dff02bf711cd 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -15,44 +15,53 @@
*/
package com.android.server.notification;
+import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
+import static android.app.Notification.FLAG_AUTO_CANCEL;
+import static android.app.Notification.FLAG_GROUP_SUMMARY;
+import static android.app.Notification.FLAG_LOCAL_ONLY;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+
+import android.annotation.NonNull;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Map;
/**
* NotificationManagerService helper for auto-grouping notifications.
*/
public class GroupHelper {
private static final String TAG = "GroupHelper";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
protected static final String AUTOGROUP_KEY = "ranker_group";
+ // Flags that all autogroup summaries have
+ protected static final int BASE_FLAGS =
+ FLAG_AUTOGROUP_SUMMARY | FLAG_GROUP_SUMMARY | FLAG_LOCAL_ONLY;
+ // Flag that autogroup summaries inherits if all children have the flag
+ private static final int ALL_CHILDREN_FLAG = FLAG_AUTO_CANCEL;
+ // Flags that autogroup summaries inherits if any child has them
+ private static final int ANY_CHILDREN_FLAGS = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
+
private final Callback mCallback;
private final int mAutoGroupAtCount;
- // count the number of ongoing notifications per group
- // userId|packageName -> (set of ongoing notifications that aren't in an app group)
- final ArrayMap<String, ArraySet<String>>
- mOngoingGroupCount = new ArrayMap<>();
-
- // Map of user : <Map of package : notification keys>. Only contains notifications that are not
- // grouped by the app (aka no group or sort key).
- Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>();
+ // Only contains notifications that are not explicitly grouped by the app (aka no group or
+ // sort key).
+ // userId|packageName -> (keys of notifications that aren't in an explicit app group -> flags)
+ @GuardedBy("mUngroupedNotifications")
+ private final ArrayMap<String, ArrayMap<String, Integer>> mUngroupedNotifications
+ = new ArrayMap<>();
public GroupHelper(int autoGroupAtCount, Callback callback) {
mAutoGroupAtCount = autoGroupAtCount;
- mCallback = callback;
+ mCallback = callback;
}
private String generatePackageKey(int userId, String pkg) {
@@ -60,69 +69,30 @@ public class GroupHelper {
}
@VisibleForTesting
- protected int getOngoingGroupCount(int userId, String pkg) {
- String key = generatePackageKey(userId, pkg);
- return mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0)).size();
- }
-
- private void updateOngoingGroupCount(StatusBarNotification sbn, boolean add) {
- if (sbn.getNotification().isGroupSummary()) {
- return;
- }
- String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName());
- ArraySet<String> notifications = mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0));
- if (add) {
- notifications.add(sbn.getKey());
- mOngoingGroupCount.put(key, notifications);
- } else {
- notifications.remove(sbn.getKey());
- // we don't need to put it back if it is default
+ @GuardedBy("mUngroupedNotifications")
+ protected int getAutogroupSummaryFlags(@NonNull final ArrayMap<String, Integer> children) {
+ boolean allChildrenHasFlag = children.size() > 0;
+ int anyChildFlagSet = 0;
+ for (int i = 0; i < children.size(); i++) {
+ if (!hasAnyFlag(children.valueAt(i), ALL_CHILDREN_FLAG)) {
+ allChildrenHasFlag = false;
+ }
+ if (hasAnyFlag(children.valueAt(i), ANY_CHILDREN_FLAGS)) {
+ anyChildFlagSet |= (children.valueAt(i) & ANY_CHILDREN_FLAGS);
+ }
}
-
- boolean needsOngoingFlag = notifications.size() > 0;
- mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), needsOngoingFlag);
+ return BASE_FLAGS | (allChildrenHasFlag ? ALL_CHILDREN_FLAG : 0) | anyChildFlagSet;
}
- public void onNotificationUpdated(StatusBarNotification childSbn) {
- updateOngoingGroupCount(childSbn, childSbn.isOngoing() && !childSbn.isAppGroup());
+ private boolean hasAnyFlag(int flags, int mask) {
+ return (flags & mask) != 0;
}
public void onNotificationPosted(StatusBarNotification sbn, boolean autogroupSummaryExists) {
try {
- updateOngoingGroupCount(sbn, sbn.isOngoing() && !sbn.isAppGroup());
-
- List<String> notificationsToGroup = new ArrayList<>();
if (!sbn.isAppGroup()) {
- // Not grouped by the app, add to the list of notifications for the app;
- // send grouping update if app exceeds the autogrouping limit.
- synchronized (mUngroupedNotifications) {
- Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser
- = mUngroupedNotifications.get(sbn.getUserId());
- if (ungroupedNotificationsByUser == null) {
- ungroupedNotificationsByUser = new HashMap<>();
- }
- mUngroupedNotifications.put(sbn.getUserId(), ungroupedNotificationsByUser);
- LinkedHashSet<String> notificationsForPackage
- = ungroupedNotificationsByUser.get(sbn.getPackageName());
- if (notificationsForPackage == null) {
- notificationsForPackage = new LinkedHashSet<>();
- }
-
- notificationsForPackage.add(sbn.getKey());
- ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage);
-
- if (notificationsForPackage.size() >= mAutoGroupAtCount
- || autogroupSummaryExists) {
- notificationsToGroup.addAll(notificationsForPackage);
- }
- }
- if (notificationsToGroup.size() > 0) {
- adjustAutogroupingSummary(sbn.getUserId(), sbn.getPackageName(),
- notificationsToGroup.get(0), true);
- adjustNotificationBundling(notificationsToGroup, true);
- }
+ maybeGroup(sbn, autogroupSummaryExists);
} else {
- // Grouped, but not by us. Send updates to un-autogroup, if we grouped it.
maybeUngroup(sbn, false, sbn.getUserId());
}
@@ -133,7 +103,6 @@ public class GroupHelper {
public void onNotificationRemoved(StatusBarNotification sbn) {
try {
- updateOngoingGroupCount(sbn, false);
maybeUngroup(sbn, true, sbn.getUserId());
} catch (Exception e) {
Slog.e(TAG, "Error processing canceled notification", e);
@@ -141,70 +110,114 @@ public class GroupHelper {
}
/**
- * Un-autogroups notifications that are now grouped by the app.
+ * A non-app grouped notification has been added or updated
+ * Evaluate if:
+ * (a) an existing autogroup summary needs updated flags
+ * (b) a new autogroup summary needs to be added with correct flags
+ * (c) other non-app grouped children need to be moved to the autogroup
+ *
+ * And stores the list of upgrouped notifications & their flags
+ */
+ private void maybeGroup(StatusBarNotification sbn, boolean autogroupSummaryExists) {
+ int flags = 0;
+ List<String> notificationsToGroup = new ArrayList<>();
+ synchronized (mUngroupedNotifications) {
+ String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName());
+ final ArrayMap<String, Integer> children =
+ mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
+
+ children.put(sbn.getKey(), sbn.getNotification().flags);
+ mUngroupedNotifications.put(key, children);
+
+ if (children.size() >= mAutoGroupAtCount || autogroupSummaryExists) {
+ flags = getAutogroupSummaryFlags(children);
+ notificationsToGroup.addAll(children.keySet());
+ }
+ }
+ if (notificationsToGroup.size() > 0) {
+ if (autogroupSummaryExists) {
+ mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), flags);
+ } else {
+ mCallback.addAutoGroupSummary(
+ sbn.getUserId(), sbn.getPackageName(), sbn.getKey(), flags);
+ }
+ for (String key : notificationsToGroup) {
+ mCallback.addAutoGroup(key);
+ }
+ }
+ }
+
+ /**
+ * A notification was added that's app grouped, or a notification was removed.
+ * Evaluate whether:
+ * (a) an existing autogroup summary needs updated flags
+ * (b) if we need to remove our autogroup overlay for this notification
+ * (c) we need to remove the autogroup summary
+ *
+ * And updates the internal state of un-app-grouped notifications and their flags
*/
private void maybeUngroup(StatusBarNotification sbn, boolean notificationGone, int userId) {
- List<String> notificationsToUnAutogroup = new ArrayList<>();
boolean removeSummary = false;
+ int summaryFlags = 0;
+ boolean updateSummaryFlags = false;
+ boolean removeAutogroupOverlay = false;
synchronized (mUngroupedNotifications) {
- Map<String, LinkedHashSet<String>> ungroupedNotificationsByUser
- = mUngroupedNotifications.get(sbn.getUserId());
- if (ungroupedNotificationsByUser == null || ungroupedNotificationsByUser.size() == 0) {
- return;
- }
- LinkedHashSet<String> notificationsForPackage
- = ungroupedNotificationsByUser.get(sbn.getPackageName());
- if (notificationsForPackage == null || notificationsForPackage.size() == 0) {
+ String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName());
+ final ArrayMap<String, Integer> children =
+ mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
+ if (children.size() == 0) {
return;
}
- if (notificationsForPackage.remove(sbn.getKey())) {
- if (!notificationGone) {
- // Add the current notification to the ungrouping list if it still exists.
- notificationsToUnAutogroup.add(sbn.getKey());
+
+ // if this notif was autogrouped and now isn't
+ if (children.containsKey(sbn.getKey())) {
+ // if this notification was contributing flags that aren't covered by other
+ // children to the summary, reevaluate flags for the summary
+ int flags = children.remove(sbn.getKey());
+ // this
+ if (hasAnyFlag(flags, ANY_CHILDREN_FLAGS)) {
+ updateSummaryFlags = true;
+ summaryFlags = getAutogroupSummaryFlags(children);
+ }
+ // if this notification still exists and has an autogroup overlay, but is now
+ // grouped by the app, clear the overlay
+ if (!notificationGone && sbn.getOverrideGroupKey() != null) {
+ removeAutogroupOverlay = true;
+ }
+
+ // If there are no more children left to autogroup, remove the summary
+ if (children.size() == 0) {
+ removeSummary = true;
}
- }
- // If the status change of this notification has brought the number of loose
- // notifications to zero, remove the summary and un-autogroup.
- if (notificationsForPackage.size() == 0) {
- ungroupedNotificationsByUser.remove(sbn.getPackageName());
- removeSummary = true;
}
}
if (removeSummary) {
- adjustAutogroupingSummary(userId, sbn.getPackageName(), null, false);
- }
- if (notificationsToUnAutogroup.size() > 0) {
- adjustNotificationBundling(notificationsToUnAutogroup, false);
- }
- }
-
- private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey,
- boolean summaryNeeded) {
- if (summaryNeeded) {
- mCallback.addAutoGroupSummary(userId, packageName, triggeringKey,
- getOngoingGroupCount(userId, packageName) > 0);
+ mCallback.removeAutoGroupSummary(userId, sbn.getPackageName());
} else {
- mCallback.removeAutoGroupSummary(userId, packageName);
+ if (updateSummaryFlags) {
+ mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), summaryFlags);
+ }
+ }
+ if (removeAutogroupOverlay) {
+ mCallback.removeAutoGroup(sbn.getKey());
}
}
- private void adjustNotificationBundling(List<String> keys, boolean group) {
- for (String key : keys) {
- if (DEBUG) Log.i(TAG, "Sending grouping adjustment for: " + key + " group? " + group);
- if (group) {
- mCallback.addAutoGroup(key);
- } else {
- mCallback.removeAutoGroup(key);
- }
+ @VisibleForTesting
+ int getNotGroupedByAppCount(int userId, String pkg) {
+ synchronized (mUngroupedNotifications) {
+ String key = generatePackageKey(userId, pkg);
+ final ArrayMap<String, Integer> children =
+ mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
+ return children.size();
}
}
protected interface Callback {
void addAutoGroup(String key);
void removeAutoGroup(String key);
- void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
- boolean needsOngoingFlag);
+ void addAutoGroupSummary(int userId, String pkg, String triggeringKey, int flags);
void removeAutoGroupSummary(int user, String pkg);
- void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag);
+ void updateAutogroupSummary(int userId, String pkg, int flags);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5d81dda5f5eb..1301cd476c26 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -319,6 +319,7 @@ import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.powerstats.StatsPullAtomCallbackImpl;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.Slogf;
@@ -902,11 +903,11 @@ public class NotificationManagerService extends SystemService {
* has the same flag. It will delete the flag otherwise
* @param userId user id of the autogroup summary
* @param pkg package of the autogroup summary
- * @param needsOngoingFlag true if the group has at least one ongoing notification
+ * @param flags the new flags for this summary
* @param isAppForeground true if the app is currently in the foreground.
*/
@GuardedBy("mNotificationLock")
- protected void updateAutobundledSummaryFlags(int userId, String pkg, boolean needsOngoingFlag,
+ protected void updateAutobundledSummaryFlags(int userId, String pkg, int flags,
boolean isAppForeground) {
ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
if (summaries == null) {
@@ -921,13 +922,8 @@ public class NotificationManagerService extends SystemService {
return;
}
int oldFlags = summary.getSbn().getNotification().flags;
- if (needsOngoingFlag) {
- summary.getSbn().getNotification().flags |= FLAG_ONGOING_EVENT;
- } else {
- summary.getSbn().getNotification().flags &= ~FLAG_ONGOING_EVENT;
- }
-
- if (summary.getSbn().getNotification().flags != oldFlags) {
+ if (oldFlags != flags) {
+ summary.getSbn().getNotification().flags = flags;
mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground,
SystemClock.elapsedRealtime()));
}
@@ -2684,9 +2680,14 @@ public class NotificationManagerService extends SystemService {
@Override
public void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
- boolean needsOngoingFlag) {
- NotificationManagerService.this.addAutoGroupSummary(
- userId, pkg, triggeringKey, needsOngoingFlag);
+ int flags) {
+ NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey, flags);
+ if (r != null) {
+ final boolean isAppForeground =
+ mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
+ mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground,
+ SystemClock.elapsedRealtime()));
+ }
}
@Override
@@ -2697,11 +2698,11 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag) {
+ public void updateAutogroupSummary(int userId, String pkg, int flags) {
boolean isAppForeground = pkg != null
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
- updateAutobundledSummaryFlags(userId, pkg, needsOngoingFlag, isAppForeground);
+ updateAutobundledSummaryFlags(userId, pkg, flags, isAppForeground);
}
}
});
@@ -5961,19 +5962,6 @@ public class NotificationManagerService extends SystemService {
r.addAdjustment(adjustment);
}
- @VisibleForTesting
- void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
- boolean needsOngoingFlag) {
- NotificationRecord r = createAutoGroupSummary(
- userId, pkg, triggeringKey, needsOngoingFlag);
- if (r != null) {
- final boolean isAppForeground =
- mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
- mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground,
- SystemClock.elapsedRealtime()));
- }
- }
-
// Clears the 'fake' auto-group summary.
@VisibleForTesting
@GuardedBy("mNotificationLock")
@@ -5997,7 +5985,7 @@ public class NotificationManagerService extends SystemService {
// Creates a 'fake' summary for a package that has exceeded the solo-notification limit.
NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey,
- boolean needsOngoingFlag) {
+ int flagsToSet) {
NotificationRecord summaryRecord = null;
boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
synchronized (mNotificationLock) {
@@ -6007,7 +5995,6 @@ public class NotificationManagerService extends SystemService {
// adjustment will post a summary if needed.
return null;
}
- NotificationChannel channel = notificationRecord.getChannel();
final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
userId = adjustedSbn.getUser().getIdentifier();
int uid = adjustedSbn.getUid();
@@ -6030,11 +6017,8 @@ public class NotificationManagerService extends SystemService {
.setGroupSummary(true)
.setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
.setGroup(GroupHelper.AUTOGROUP_KEY)
- .setFlag(FLAG_AUTOGROUP_SUMMARY, true)
- .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
- .setFlag(FLAG_ONGOING_EVENT, needsOngoingFlag)
+ .setFlag(flagsToSet, true)
.setColor(adjustedSbn.getNotification().color)
- .setLocalOnly(true)
.build();
summaryNotification.extras.putAll(extras);
Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
@@ -6372,6 +6356,7 @@ public class NotificationManagerService extends SystemService {
* The private API only accessible to the system process.
*/
private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() {
+
@Override
public NotificationChannel getNotificationChannel(String pkg, int uid, String
channelId) {
@@ -7827,18 +7812,17 @@ public class NotificationManagerService extends SystemService {
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
mListeners.notifyPostedLocked(r, old);
- if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
- && !isCritical(r)) {
- mHandler.post(() -> {
- synchronized (mNotificationLock) {
- mGroupHelper.onNotificationPosted(
- n, hasAutoGroupSummaryLocked(n));
- }
- });
- } else if (oldSbn != null) {
- final NotificationRecord finalRecord = r;
- mHandler.post(() ->
- mGroupHelper.onNotificationUpdated(finalRecord.getSbn()));
+ if (oldSbn == null
+ || !Objects.equals(oldSbn.getGroup(), n.getGroup())
+ || oldSbn.getNotification().flags != n.getNotification().flags) {
+ if (!isCritical(r)) {
+ mHandler.post(() -> {
+ synchronized (mNotificationLock) {
+ mGroupHelper.onNotificationPosted(
+ n, hasAutoGroupSummaryLocked(n));
+ }
+ });
+ }
}
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 596e9b964643..69ef3f780172 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -994,7 +994,7 @@ final class InstallPackageHelper {
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
requests, Collections.unmodifiableMap(mPm.mPackages),
versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
- mPm.mSettings);
+ mPm.mSettings, mContext);
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.setError("Reconciliation failed...", e);
@@ -3930,7 +3930,7 @@ final class InstallPackageHelper {
mPm.mPackages, Collections.singletonMap(pkgName,
mPm.getSettingsVersionForPackage(parsedPackage)),
mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
- mPm.mSettings);
+ mPm.mSettings, mContext);
if ((scanFlags & SCAN_AS_APEX) == 0) {
appIdCreated = optimisticallyRegisterAppId(installRequest);
} else {
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 5312ae6ca84c..e3c97e933ad1 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
@@ -23,19 +24,24 @@ import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRI
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
import android.os.SystemProperties;
+import android.permission.PermissionManager;
import android.util.ArrayMap;
import android.util.Log;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.component.ParsedUsesPermission;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.WatchedLongSparseArray;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -54,7 +60,7 @@ final class ReconcilePackageUtils {
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos,
SharedLibrariesImpl sharedLibraries,
- KeySetManagerService ksms, Settings settings)
+ KeySetManagerService ksms, Settings settings, Context context)
throws ReconcileFailure {
final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());
@@ -143,11 +149,11 @@ final class ReconcilePackageUtils {
} else {
if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + parsedPackage.getPackageName()
+ "Package " + installPackageName
+ " upgrade keys do not match the previously installed"
+ " version");
} else {
- String msg = "System package " + parsedPackage.getPackageName()
+ String msg = "System package " + installPackageName
+ " signature changed; retaining data.";
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
}
@@ -168,11 +174,42 @@ final class ReconcilePackageUtils {
removeAppKeySetData = true;
}
- // if this is is a sharedUser, check to see if the new package is signed by a
- // newer
- // signing certificate than the existing one, and if so, copy over the new
+ // if this is a sharedUser, check to see if the new package is signed by a
+ // newer signing certificate than the existing one, and if so, copy over the new
// details
if (sharedUserSetting != null) {
+ if (!parsedPackage.isTestOnly() && sharedUserSetting.isPrivileged()
+ && !signatureCheckPs.isSystem()) {
+ final List<ParsedUsesPermission> usesPermissions =
+ parsedPackage.getUsesPermissions();
+ final List<String> usesPrivilegedPermissions = new ArrayList<>();
+ final PermissionManager permissionManager = context.getSystemService(
+ PermissionManager.class);
+ // Check if the app requests any privileged permissions because that
+ // violates the privapp-permissions allowlist check during boot.
+ if (permissionManager != null) {
+ for (int i = 0; i < usesPermissions.size(); i++) {
+ final String permissionName = usesPermissions.get(i).getName();
+ final PermissionInfo permissionInfo =
+ permissionManager.getPermissionInfo(permissionName, 0);
+ if (permissionInfo != null
+ && (permissionInfo.getProtectionFlags()
+ & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
+ usesPrivilegedPermissions.add(permissionName);
+ }
+ }
+ }
+
+ if (!usesPrivilegedPermissions.isEmpty()) {
+ throw new ReconcileFailure(INSTALL_FAILED_INVALID_APK,
+ "Non-system package: " + installPackageName
+ + " shares signature and sharedUserId with"
+ + " a privileged package but requests"
+ + " privileged permissions that are not"
+ + " allowed: " + Arrays.toString(
+ usesPrivilegedPermissions.toArray()));
+ }
+ }
// Attempt to merge the existing lineage for the shared SigningDetails with
// the lineage of the new package; if the shared SigningDetails are not
// returned this indicates the new package added new signers to the lineage
@@ -189,7 +226,7 @@ final class ReconcilePackageUtils {
for (AndroidPackage androidPackage : sharedUserSetting.getPackages()) {
if (androidPackage.getPackageName() != null
&& !androidPackage.getPackageName().equals(
- parsedPackage.getPackageName())) {
+ installPackageName)) {
mergedDetails = mergedDetails.mergeLineageWith(
androidPackage.getSigningDetails(),
MERGE_RESTRICTED_CAPABILITY);
@@ -219,7 +256,7 @@ final class ReconcilePackageUtils {
if (sharedUserSetting != null) {
if (sharedUserSetting.signaturesChanged != null
&& !PackageManagerServiceUtils.canJoinSharedUserId(
- parsedPackage.getPackageName(), parsedPackage.getSigningDetails(),
+ installPackageName, parsedPackage.getSigningDetails(),
sharedUserSetting,
PackageManagerServiceUtils.SHARED_USER_ID_JOIN_TYPE_SYSTEM)) {
if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
@@ -240,7 +277,7 @@ final class ReconcilePackageUtils {
// whichever package happened to be scanned later.
throw new IllegalStateException(
"Signature mismatch on system package "
- + parsedPackage.getPackageName()
+ + installPackageName
+ " for shared user "
+ sharedUserSetting);
}
@@ -252,7 +289,7 @@ final class ReconcilePackageUtils {
sharedUserSetting.signaturesChanged = Boolean.TRUE;
}
// File a report about this.
- String msg = "System package " + parsedPackage.getPackageName()
+ String msg = "System package " + installPackageName
+ " signature changed; retaining data.";
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
} catch (IllegalArgumentException e) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e5e32f0a9690..4d2b119d7800 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -206,8 +206,6 @@ final class DefaultPermissionGrantPolicy {
static {
SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
}
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index c5f939a2a66e..297ad73e054b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1332,9 +1332,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Bg location is one-off runtime modifier permission and has no app op
if (sPlatformPermissions.containsKey(permission)
&& !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)
- && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)
- && !Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND
- .equals(permission)) {
+ && !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) {
Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
+ " with no app op defined!");
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index cc2c9adfcba4..3492b2660c4a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -23,6 +23,7 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT;
+import static android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED;
import static android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
@@ -3655,6 +3656,26 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
for (String permission : pkg.getRequestedPermissions()) {
Integer permissionState = permissionStates.get(permission);
+
+ if (Objects.equals(permission, Manifest.permission.USE_FULL_SCREEN_INTENT)
+ && permissionState == null) {
+ final PackageStateInternal ps;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ps = mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ final String[] useFullScreenIntentPackageNames =
+ mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_useFullScreenIntentPackages);
+ final boolean canUseFullScreenIntent = (ps != null && ps.isSystem())
+ || ArrayUtils.contains(useFullScreenIntentPackageNames,
+ pkg.getPackageName());
+ permissionState = canUseFullScreenIntent ? PERMISSION_STATE_GRANTED
+ : PERMISSION_STATE_DENIED;
+ }
+
if (permissionState == null || permissionState == PERMISSION_STATE_DEFAULT) {
continue;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8165958bd4ef..fc6b4e9dcb20 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
@@ -412,6 +413,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
SensorPrivacyManager mSensorPrivacyManager;
DisplayManager mDisplayManager;
DisplayManagerInternal mDisplayManagerInternal;
+
+ private WallpaperManagerInternal mWallpaperManagerInternal;
+
boolean mPreloadedRecentApps;
final Object mServiceAcquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -5016,11 +5020,34 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return bootCompleted ? mKeyguardDrawnTimeout : 5000;
}
+ @Nullable
+ private WallpaperManagerInternal getWallpaperManagerInternal() {
+ if (mWallpaperManagerInternal == null) {
+ mWallpaperManagerInternal = LocalServices.getService(WallpaperManagerInternal.class);
+ }
+ return mWallpaperManagerInternal;
+ }
+
+ private void reportScreenTurningOnToWallpaper(int displayId) {
+ WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
+ if (wallpaperManagerInternal != null) {
+ wallpaperManagerInternal.onScreenTurningOn(displayId);
+ }
+ }
+
+ private void reportScreenTurnedOnToWallpaper(int displayId) {
+ WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
+ if (wallpaperManagerInternal != null) {
+ wallpaperManagerInternal.onScreenTurnedOn(displayId);
+ }
+ }
+
// Called on the DisplayManager's DisplayPowerController thread.
@Override
public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");
+ reportScreenTurningOnToWallpaper(displayId);
if (displayId == DEFAULT_DISPLAY) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
0 /* cookie */);
@@ -5061,6 +5088,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void screenTurnedOn(int displayId) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on...");
+ reportScreenTurnedOnToWallpaper(displayId);
+
if (displayId != DEFAULT_DISPLAY) {
return;
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 7beb1edfe51f..e437be8e01b5 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -105,36 +105,46 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
- // For native crashes, we will roll back any available rollbacks
+ boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class)
+ .getAvailableRollbacks().isEmpty();
+ int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+
if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
- && !mContext.getSystemService(RollbackManager.class)
- .getAvailableRollbacks().isEmpty()) {
- return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
- }
- if (getAvailableRollback(failedPackage) == null) {
- // Don't handle the notification, no rollbacks available for the package
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
- } else {
+ && anyRollbackAvailable) {
+ // For native crashes, we will directly roll back any available rollbacks
+ // Note: For non-native crashes the rollback-all step has higher impact
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ } else if (mitigationCount == 1 && getAvailableRollback(failedPackage) != null) {
// Rollback is available, we may get a callback into #execute
- return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ } else if (mitigationCount > 1 && anyRollbackAvailable) {
+ // If any rollbacks are available, we will commit them
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
}
+
+ return impact;
}
@Override
public boolean execute(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason, int mitigationCount) {
if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
- mHandler.post(() -> rollbackAll());
+ mHandler.post(() -> rollbackAll(rollbackReason));
return true;
}
- RollbackInfo rollback = getAvailableRollback(failedPackage);
- if (rollback == null) {
- Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
- return false;
+ if (mitigationCount == 1) {
+ RollbackInfo rollback = getAvailableRollback(failedPackage);
+ if (rollback == null) {
+ Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
+ return false;
+ }
+ mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
+ } else if (mitigationCount > 1) {
+ mHandler.post(() -> rollbackAll(rollbackReason));
}
- mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
- // Assume rollback executed successfully
+
+ // Assume rollbacks executed successfully
return true;
}
@@ -468,7 +478,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
}
@WorkerThread
- private void rollbackAll() {
+ private void rollbackAll(@FailureReasons int rollbackReason) {
assertInWorkerThread();
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
@@ -487,7 +497,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
for (RollbackInfo rollback : rollbacks) {
VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
- rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+ rollbackPackage(rollback, sample, rollbackReason);
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
index 584fbddee478..3699557706fd 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
@@ -27,4 +27,10 @@ public abstract class WallpaperManagerInternal {
* Notifies the display is ready for adding wallpaper on it.
*/
public abstract void onDisplayReady(int displayId);
+
+ /** Notifies when the screen finished turning on and is visible to the user. */
+ public abstract void onScreenTurnedOn(int displayId);
+
+ /** Notifies when the screen starts turning on and is not yet visible to the user. */
+ public abstract void onScreenTurningOn(int displayId);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 4a03628ab8e5..e17866922990 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1613,6 +1613,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public void onDisplayReady(int displayId) {
onDisplayReadyInternal(displayId);
}
+
+ @Override
+ public void onScreenTurnedOn(int displayId) {
+ notifyScreenTurnedOn(displayId);
+ }
+ @Override
+ public void onScreenTurningOn(int displayId) {
+ notifyScreenTurningOn(displayId);
+ }
}
void initialize() {
@@ -2442,6 +2451,54 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ /**
+ * Propagates screen turned on event to wallpaper engine.
+ */
+ @Override
+ public void notifyScreenTurnedOn(int displayId) {
+ synchronized (mLock) {
+ final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ if (data != null
+ && data.connection != null
+ && data.connection.containsDisplay(displayId)) {
+ final IWallpaperEngine engine = data.connection
+ .getDisplayConnectorOrCreate(displayId).mEngine;
+ if (engine != null) {
+ try {
+ engine.onScreenTurnedOn();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Propagate screen turning on event to wallpaper engine.
+ */
+ @Override
+ public void notifyScreenTurningOn(int displayId) {
+ synchronized (mLock) {
+ final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ if (data != null
+ && data.connection != null
+ && data.connection.containsDisplay(displayId)) {
+ final IWallpaperEngine engine = data.connection
+ .getDisplayConnectorOrCreate(displayId).mEngine;
+ if (engine != null) {
+ try {
+ engine.onScreenTurningOn();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
@Override
public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 62144401223b..7718dd88b772 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1145,8 +1145,7 @@ class ActivityClientController extends IActivityClientController.Stub {
if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Multiwindow Fullscreen Request: %s", transition);
- mService.mWindowManager.mSyncEngine.queueSyncSet(
- () -> r.mTransitionController.moveToCollecting(transition),
+ r.mTransitionController.queueCollecting(transition,
() -> {
executeFullscreenRequestTransition(fullscreenRequest, callback, r,
transition, true /* queued */);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1bcc05e6f116..0b98495c8e99 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9820,8 +9820,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
scheduleStopForRestartProcess();
};
if (mWmService.mSyncEngine.hasActiveSync()) {
- mWmService.mSyncEngine.queueSyncSet(
- () -> mTransitionController.moveToCollecting(transition), executeRestart);
+ mTransitionController.queueCollecting(transition, executeRestart);
} else {
mTransitionController.moveToCollecting(transition);
executeRestart.run();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8123c07052b0..e780716dd06c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2874,8 +2874,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
getTransitionController(), mWindowManager.mSyncEngine);
if (mWindowManager.mSyncEngine.hasActiveSync()) {
- mWindowManager.mSyncEngine.queueSyncSet(
- () -> getTransitionController().moveToCollecting(transition),
+ getTransitionController().queueCollecting(transition,
() -> {
if (!task.getWindowConfiguration().canResizeTask()) {
Slog.w(TAG, "resizeTask not allowed on task=" + task);
@@ -3629,9 +3628,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Pip-Enter: %s", transition);
- mWindowManager.mSyncEngine.queueSyncSet(
- () -> getTransitionController().moveToCollecting(transition),
- enterPipRunnable);
+ getTransitionController().queueCollecting(transition, enterPipRunnable);
} else {
// Move to collecting immediately to "claim" the sync-engine for this
// transition.
@@ -3647,9 +3644,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Pip-Enter: %s", transition);
- mWindowManager.mSyncEngine.queueSyncSet(
- () -> getTransitionController().moveToCollecting(transition),
- enterPipRunnable);
+ getTransitionController().queueCollecting(transition, enterPipRunnable);
} else {
if (transition != null) {
getTransitionController().moveToCollecting(transition);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 7b562b0bc964..745374301263 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -648,31 +648,28 @@ class BackNavigationController {
if (finishedTransition == mWaitTransitionFinish) {
clearBackAnimations();
}
+
if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
return false;
}
-
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Handling the deferred animation after transition finished");
- // Show the target surface and its parents to prevent it or its parents hidden when
- // the transition finished.
- // The target could be affected by transition when :
- // Open transition -> the open target in back navigation
- // Close transition -> the close target in back navigation.
+ // Find the participated container collected by transition when :
+ // Open transition -> the open target in back navigation, the close target in transition.
+ // Close transition -> the close target in back navigation, the open target in transition.
boolean hasTarget = false;
- final SurfaceControl.Transaction t =
- mPendingAnimationBuilder.mCloseTarget.getPendingTransaction();
- for (int i = 0; i < targets.size(); i++) {
- final WindowContainer wc = targets.get(i).mContainer;
- if (wc.asActivityRecord() == null && wc.asTask() == null) {
- continue;
- } else if (!mPendingAnimationBuilder.containTarget(wc)) {
+ for (int i = 0; i < finishedTransition.mParticipants.size(); i++) {
+ final WindowContainer wc = finishedTransition.mParticipants.valueAt(i);
+ if (wc.asActivityRecord() == null && wc.asTask() == null
+ && wc.asTaskFragment() == null) {
continue;
}
- hasTarget = true;
- t.show(wc.getSurfaceControl());
+ if (mPendingAnimationBuilder.containTarget(wc)) {
+ hasTarget = true;
+ break;
+ }
}
if (!hasTarget) {
@@ -689,6 +686,12 @@ class BackNavigationController {
return false;
}
+ // Ensure the final animation targets which hidden by transition could be visible.
+ for (int i = 0; i < targets.size(); i++) {
+ final WindowContainer wc = targets.get(i).mContainer;
+ wc.prepareSurfaces();
+ }
+
scheduleAnimation(mPendingAnimationBuilder);
mPendingAnimationBuilder = null;
return true;
@@ -1076,7 +1079,7 @@ class BackNavigationController {
boolean containTarget(@NonNull WindowContainer wc) {
return wc == mOpenTarget || wc == mCloseTarget
- || wc.hasChild(mOpenTarget) || wc.hasChild(mCloseTarget);
+ || mOpenTarget.hasChild(wc) || mCloseTarget.hasChild(wc);
}
/**
@@ -1151,6 +1154,11 @@ class BackNavigationController {
private static void setLaunchBehind(@NonNull ActivityRecord activity) {
if (!activity.isVisibleRequested()) {
activity.setVisibility(true);
+ // The transition could commit the visibility and in the finishing state, that could
+ // skip commitVisibility call in setVisibility cause the activity won't visible here.
+ // Call it again to make sure the activity could be visible while handling the pending
+ // animation.
+ activity.commitVisibility(true, true);
}
activity.mLaunchTaskBehind = true;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 57fca3aeea79..237846997e9e 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -146,8 +146,6 @@ public class RecentsAnimationController implements DeathRecipient {
@VisibleForTesting
boolean mIsAddingTaskToTargets;
- @VisibleForTesting
- boolean mShouldAttachNavBarToAppDuringTransition;
private boolean mNavigationBarAttachedToApp;
private ActivityRecord mNavBarAttachedApp;
@@ -379,8 +377,6 @@ public class RecentsAnimationController implements DeathRecipient {
mDisplayId = displayId;
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
mDisplayContent = service.mRoot.getDisplayContent(displayId);
- mShouldAttachNavBarToAppDuringTransition =
- mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition();
}
/**
@@ -577,7 +573,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
private void attachNavigationBarToApp() {
- if (!mShouldAttachNavBarToAppDuringTransition
+ if (!mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
// Skip the case where the nav bar is controlled by fade rotation.
|| mDisplayContent.getAsyncRotationController() != null) {
return;
@@ -652,7 +648,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
void animateNavigationBarForAppLaunch(long duration) {
- if (!mShouldAttachNavBarToAppDuringTransition
+ if (!mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
// Skip the case where the nav bar is controlled by fade rotation.
|| mDisplayContent.getAsyncRotationController() != null
|| mNavigationBarAttachedToApp
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 07daa4b22ac9..ad934541267e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2335,9 +2335,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
transition.playNow();
};
if (display.mTransitionController.isCollecting()) {
- mWmService.mSyncEngine.queueSyncSet(
- () -> display.mTransitionController.moveToCollecting(transition),
- sendSleepTransition);
+ display.mTransitionController.queueCollecting(transition, sendSleepTransition);
} else {
display.mTransitionController.moveToCollecting(transition);
sendSleepTransition.run();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fb592e124920..89f975387667 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5635,8 +5635,7 @@ class Task extends TaskFragment {
if (mWmService.mSyncEngine.hasActiveSync()) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Move-to-back: %s", transition);
- mWmService.mSyncEngine.queueSyncSet(
- () -> mTransitionController.moveToCollecting(transition),
+ mTransitionController.queueCollecting(transition,
() -> {
// Need to check again since this happens later and the system might
// be in a different state.
@@ -5684,6 +5683,7 @@ class Task extends TaskFragment {
// Usually resuming a top activity triggers the next app transition, but nothing's got
// resumed in this case, so we need to execute it explicitly.
mDisplayContent.executeAppTransition();
+ mDisplayContent.setFocusedApp(topActivity);
} else {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3a909cedc5ab..652c2977f40e 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -287,7 +287,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (restoreBelow != null) {
final Task transientRootTask = activity.getRootTask();
- // Collect all visible activities which can be occluded by the transient activity to
+ // Collect all visible tasks which can be occluded by the transient activity to
// make sure they are in the participants so their visibilities can be updated when
// finishing transition.
((WindowContainer<?>) restoreBelow.getParent()).forAllTasks(t -> {
@@ -297,11 +297,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mTransientHideTasks.add(t);
}
if (t.isLeafTask()) {
- t.forAllActivities(r -> {
- if (r.isVisibleRequested()) {
- collect(r);
- }
- });
+ collect(t);
}
}
return t == restoreBelow;
@@ -904,6 +900,18 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mController.mFinishingTransition = this;
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
+ // Record all the now-hiding activities so that they are committed after
+ // recalculating visibilities. We just use mParticipants because we can and it will
+ // ensure proper reporting of `isInFinishTransition`.
+ for (int i = 0; i < mTransientHideTasks.size(); ++i) {
+ mTransientHideTasks.get(i).forAllActivities(r -> {
+ // Only check leaf-tasks that were collected
+ if (!mParticipants.contains(r.getTask())) return;
+ // Only concern ourselves with anything that can become invisible
+ if (!r.isVisible()) return;
+ mParticipants.add(r);
+ });
+ }
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
// step can continue to commit the visibility.
@@ -953,7 +961,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
enterAutoPip = true;
}
}
- if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) {
+ final ChangeInfo changeInfo = mChanges.get(ar);
+ // Due to transient-hide, there may be some activities here which weren't in the
+ // transition.
+ if (changeInfo != null && changeInfo.mVisible != visibleAtTransitionEnd) {
// Legacy dispatch relies on this (for now).
ar.mEnteringAnimation = visibleAtTransitionEnd;
} else if (mTransientLaunches != null && mTransientLaunches.containsKey(ar)
@@ -1609,7 +1620,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" id=" + mSyncId);
sb.append(" type=" + transitTypeToString(mType));
- sb.append(" flags=" + mFlags);
+ sb.append(" flags=0x" + Integer.toHexString(mFlags));
sb.append('}');
return sb.toString();
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 3bf896939f49..4c1c2ff1ac10 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -443,26 +443,36 @@ class TransitionController {
return type == TRANSIT_OPEN || type == TRANSIT_CLOSE;
}
- /** Whether the display change should run with blast sync. */
- private static boolean shouldSync(@NonNull TransitionRequestInfo.DisplayChange displayChange) {
- if ((displayChange.getStartRotation() + displayChange.getEndRotation()) % 2 == 0) {
+ /** Sets the sync method for the display change. */
+ private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
+ @NonNull Transition displayTransition, @NonNull DisplayContent displayContent) {
+ final int startRotation = displayChange.getStartRotation();
+ final int endRotation = displayChange.getEndRotation();
+ if (startRotation != endRotation && (startRotation + endRotation) % 2 == 0) {
// 180 degrees rotation change may not change screen size. So the clients may draw
// some frames before and after the display projection transaction is applied by the
// remote player. That may cause some buffers to show in different rotation. So use
// sync method to pause clients drawing until the projection transaction is applied.
- return true;
+ mAtm.mWindowManager.mSyncEngine.setSyncMethod(displayTransition.getSyncId(),
+ BLASTSyncEngine.METHOD_BLAST);
}
final Rect startBounds = displayChange.getStartAbsBounds();
final Rect endBounds = displayChange.getEndAbsBounds();
- if (startBounds == null || endBounds == null) return false;
+ if (startBounds == null || endBounds == null) return;
final int startWidth = startBounds.width();
final int startHeight = startBounds.height();
final int endWidth = endBounds.width();
final int endHeight = endBounds.height();
// This is changing screen resolution. Because the screen decor layers are excluded from
// screenshot, their draw transactions need to run with the start transaction.
- return (endWidth > startWidth) == (endHeight > startHeight)
- && (endWidth != startWidth || endHeight != startHeight);
+ if ((endWidth > startWidth) == (endHeight > startHeight)
+ && (endWidth != startWidth || endHeight != startHeight)) {
+ displayContent.forAllWindows(w -> {
+ if (w.mToken.mRoundedCornerOverlay && w.mHasSurface) {
+ w.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
+ }
+ }, true /* traverseTopToBottom */);
+ }
}
/**
@@ -494,9 +504,9 @@ class TransitionController {
} else {
newTransition = requestStartTransition(createTransition(type, flags),
trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
- if (newTransition != null && displayChange != null && shouldSync(displayChange)) {
- mAtm.mWindowManager.mSyncEngine.setSyncMethod(newTransition.getSyncId(),
- BLASTSyncEngine.METHOD_BLAST);
+ if (newTransition != null && displayChange != null && trigger != null
+ && trigger.asDisplayContent() != null) {
+ setDisplaySyncMethod(displayChange, newTransition, trigger.asDisplayContent());
}
}
if (trigger != null) {
@@ -874,6 +884,15 @@ class TransitionController {
proto.end(token);
}
+ void queueCollecting(Transition transit, Runnable onCollectStart) {
+ mAtm.mWindowManager.mSyncEngine.queueSyncSet(
+ // Make sure to collect immediately to prevent another transition
+ // from sneaking in before it. Note: moveToCollecting internally
+ // calls startSyncSet.
+ () -> moveToCollecting(transit),
+ onCollectStart);
+ }
+
/**
* This manages the animating state of processes that are running remote animations for
* {@link #mTransitionPlayerProc}.
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 32d54d774b40..f63470f2bea4 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -291,23 +291,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (type < 0) {
throw new IllegalArgumentException("Can't create transition with no type");
}
+ transition = new Transition(type, 0 /* flags */, mTransitionController,
+ mService.mWindowManager.mSyncEngine);
// If there is already a collecting transition, queue up a new transition and
// return that. The actual start and apply will then be deferred until that
// transition starts collecting. This should almost never happen except during
// tests.
if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
Slog.w(TAG, "startTransition() while one is already collecting.");
- final Transition nextTransition = new Transition(type, 0 /* flags */,
- mTransitionController, mService.mWindowManager.mSyncEngine);
+ final Transition nextTransition = transition;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Transition: %s", nextTransition);
- mService.mWindowManager.mSyncEngine.queueSyncSet(
- // Make sure to collect immediately to prevent another transition
- // from sneaking in before it. Note: moveToCollecting internally
- // calls startSyncSet.
- () -> mTransitionController.moveToCollecting(nextTransition),
+ mTransitionController.queueCollecting(nextTransition,
() -> {
nextTransition.start();
+ nextTransition.mLogger.mStartWCT = wct;
applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
if (needsSetReady) {
nextTransition.setAllReady();
@@ -315,7 +313,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
});
return nextTransition.getToken();
}
- transition = mTransitionController.createTransition(type);
+ mTransitionController.moveToCollecting(transition);
}
if (!transition.isCollecting() && !transition.isForcePlaying()) {
Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
@@ -474,11 +472,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mTransitionController, mService.mWindowManager.mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Transition for TaskFragment: %s", nextTransition);
- mService.mWindowManager.mSyncEngine.queueSyncSet(
- // Make sure to collect immediately to prevent another transition
- // from sneaking in before it. Note: moveToCollecting internally
- // calls startSyncSet.
- () -> mTransitionController.moveToCollecting(nextTransition),
+ mTransitionController.queueCollecting(nextTransition,
() -> {
if (mTaskFragmentOrganizerController.isValidTransaction(wct)
&& (applyTransaction(wct, -1 /* syncId */, nextTransition, caller)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index f111a9541303..926c7e400144 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -27,6 +27,7 @@ import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PAREN
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.BroadcastOptions;
import android.app.admin.DevicePolicyIdentifiers;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyState;
@@ -353,6 +354,7 @@ final class DevicePolicyEngine {
policyDefinition,
userId);
}
+ sendDevicePolicyChangedToSystem(userId);
}
/**
@@ -478,6 +480,8 @@ final class DevicePolicyEngine {
enforcingAdmin,
policyDefinition,
UserHandle.USER_ALL);
+
+ sendDevicePolicyChangedToSystem(UserHandle.USER_ALL);
}
/**
@@ -699,7 +703,7 @@ final class DevicePolicyEngine {
if (policyDefinition.isGlobalOnlyPolicy()) {
throw new IllegalArgumentException(policyDefinition.getPolicyKey() + " is a global only"
- + "policy.");
+ + " policy.");
}
if (!mLocalPolicies.contains(userId)) {
@@ -724,7 +728,7 @@ final class DevicePolicyEngine {
private <V> PolicyState<V> getGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) {
if (policyDefinition.isLocalOnlyPolicy()) {
throw new IllegalArgumentException(policyDefinition.getPolicyKey() + " is a local only"
- + "policy.");
+ + " policy.");
}
if (!mGlobalPolicies.containsKey(policyDefinition.getPolicyKey())) {
@@ -761,6 +765,20 @@ final class DevicePolicyEngine {
policyValue == null ? null : policyValue.getValue(), mContext, userId);
}
+ private void sendDevicePolicyChangedToSystem(int userId) {
+ Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ Bundle options = new BroadcastOptions()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+ .toBundle();
+ Binder.withCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
+ intent,
+ new UserHandle(userId),
+ /* receiverPermissions= */ null,
+ options));
+ }
+
private <V> void sendPolicyResultToAdmin(
EnforcingAdmin admin, PolicyDefinition<V> policyDefinition, int result, int userId) {
Intent intent = new Intent(PolicyUpdateReceiver.ACTION_DEVICE_POLICY_SET_RESULT);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 94e6e732dd76..29f9a306880f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.devicepolicy;
import static android.Manifest.permission.BIND_DEVICE_ADMIN;
import static android.Manifest.permission.LOCK_DEVICE;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
+import static android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL;
@@ -59,6 +60,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTI
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT;
@@ -441,7 +443,6 @@ import android.util.AtomicFile;
import android.util.DebugUtils;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -7767,12 +7768,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Explicit behaviour
if (factoryReset) {
// TODO(b/254031494) Replace with new factory reset permission checks
- boolean hasPermission = isDeviceOwnerUserId(userId)
- || (isOrganizationOwnedDeviceWithManagedProfile()
- && calledOnParentInstance);
- Preconditions.checkState(hasPermission,
- "Admin %s does not have permission to factory reset the device.",
- userId);
+ if (!isPermissionCheckFlagEnabled()) {
+ boolean hasPermission = isDeviceOwnerUserId(userId)
+ || (isOrganizationOwnedDeviceWithManagedProfile()
+ && calledOnParentInstance);
+ Preconditions.checkCallAuthorization(hasPermission,
+ "Admin %s does not have permission to factory reset the device.",
+ userId);
+ }
wipeDevice = true;
} else {
Preconditions.checkCallAuthorization(!isSystemUser,
@@ -13131,19 +13134,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
int userId = caller.getUserId();
- if (!UserRestrictionsUtils.isValidRestriction(key)) {
- return;
- }
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
if (isPolicyEngineForFinanceFlagEnabled()) {
- int affectedUserId = parent ? getProfileParentId(userId) : userId;
- EnforcingAdmin admin = enforcePermissionForUserRestriction(
- who,
- key,
- caller.getPackageName(),
- affectedUserId);
- if (mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) {
+ if (!isDeviceOwner(caller) && !isProfileOwner(caller)) {
+ if (!mInjector.isChangeEnabled(ENABLE_COEXISTENCE_CHANGE, callerPackage, userId)) {
+ throw new IllegalStateException("Calling package is not targeting Android U.");
+ }
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ throw new IllegalArgumentException("Invalid restriction key: " + key);
+ }
+ int affectedUserId = parent ? getProfileParentId(userId) : userId;
+ EnforcingAdmin admin = enforcePermissionForUserRestriction(
+ who,
+ key,
+ caller.getPackageName(),
+ affectedUserId);
PolicyDefinition<Boolean> policyDefinition =
PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
if (enabledFromThisOwner) {
@@ -13155,7 +13161,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
setGlobalUserRestrictionInternal(admin, key, /* enabled= */ false);
}
if (!policyDefinition.isGlobalOnlyPolicy()) {
- setLocalUserRestrictionInternal(admin, key, /* enabled= */ false, userId);
+ setLocalUserRestrictionInternal(admin, key, /* enabled= */ false,
+ userId);
int parentUserId = getProfileParentId(userId);
if (parentUserId != userId) {
@@ -13165,49 +13172,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
} else {
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ return;
+ }
+ Objects.requireNonNull(who, "ComponentName is null");
+ EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
+ checkAdminCanSetRestriction(caller, parent, key);
setBackwardCompatibleUserRestriction(
caller, admin, key, enabledFromThisOwner, parent);
}
} else {
- Objects.requireNonNull(who, "ComponentName is null");
- if (parent) {
- Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(caller));
- } else {
- Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller));
- }
- synchronized (getLockObject()) {
- if (isDefaultDeviceOwner(caller)) {
- if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
- throw new SecurityException("Device owner cannot set user restriction "
- + key);
- }
- Preconditions.checkArgument(!parent,
- "Cannot use the parent instance in Device Owner mode");
- } else if (isFinancedDeviceOwner(caller)) {
- if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) {
- throw new SecurityException("Cannot set user restriction " + key
- + " when managing a financed device");
- }
- Preconditions.checkArgument(!parent,
- "Cannot use the parent instance in Financed Device Owner"
- + " mode");
- } else {
- boolean profileOwnerCanChangeOnItself = !parent
- && UserRestrictionsUtils.canProfileOwnerChange(
- key, userId == getMainUserId());
- boolean orgOwnedProfileOwnerCanChangeGlobally = parent
- && isProfileOwnerOfOrganizationOwnedDevice(caller)
- && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
- key);
-
- if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) {
- throw new SecurityException("Profile owner cannot set user restriction "
- + key);
- }
- }
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ return;
}
+ Objects.requireNonNull(who, "ComponentName is null");
+ checkAdminCanSetRestriction(caller, parent, key);
synchronized (getLockObject()) {
final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
getProfileOwnerOrDeviceOwnerLocked(userId), parent);
@@ -13224,6 +13203,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
}
+ private void checkAdminCanSetRestriction(CallerIdentity caller, boolean parent, String key) {
+ if (parent) {
+ Preconditions.checkCallAuthorization(
+ isProfileOwnerOfOrganizationOwnedDevice(caller));
+ } else {
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwner(caller));
+ }
+ synchronized (getLockObject()) {
+ if (isDefaultDeviceOwner(caller)) {
+ if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
+ throw new SecurityException("Device owner cannot set user restriction "
+ + key);
+ }
+ Preconditions.checkArgument(!parent,
+ "Cannot use the parent instance in Device Owner mode");
+ } else if (isFinancedDeviceOwner(caller)) {
+ if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) {
+ throw new SecurityException("Cannot set user restriction " + key
+ + " when managing a financed device");
+ }
+ Preconditions.checkArgument(!parent,
+ "Cannot use the parent instance in Financed Device Owner"
+ + " mode");
+ } else {
+ boolean profileOwnerCanChangeOnItself = !parent
+ && UserRestrictionsUtils.canProfileOwnerChange(
+ key, caller.getUserId() == getMainUserId());
+ boolean orgOwnedProfileOwnerCanChangeGlobally = parent
+ && isProfileOwnerOfOrganizationOwnedDevice(caller)
+ && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
+ key);
+
+ if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) {
+ throw new SecurityException("Profile owner cannot set user restriction "
+ + key);
+ }
+ }
+ }
+ }
private void setBackwardCompatibleUserRestriction(
CallerIdentity caller, EnforcingAdmin admin, String key, boolean enabled,
boolean parent) {
@@ -13252,20 +13271,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setUserRestrictionGlobally(String callerPackage, String key) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
- if (!UserRestrictionsUtils.isValidRestriction(key)) {
- return;
- }
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
if (!isPolicyEngineForFinanceFlagEnabled()) {
throw new IllegalStateException("Feature flag is not enabled.");
}
-
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ throw new IllegalStateException("Admins are not allowed to call this API.");
+ }
if (!mInjector.isChangeEnabled(
ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
throw new IllegalStateException("Calling package is not targeting Android U.");
}
+ if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ throw new IllegalArgumentException("Invalid restriction key: " + key);
+ }
EnforcingAdmin admin = enforcePermissionForUserRestriction(
/* who= */ null,
@@ -13416,14 +13437,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int targetUserId = parent
? getProfileParentId(caller.getUserId()) : caller.getUserId();
EnforcingAdmin admin = getEnforcingAdminForCaller(who, callerPackage);
- Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId);
- // Add global restrictions set by the admin as well if admin is not targeting Android U.
- if (!mInjector.isChangeEnabled(
- ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ Objects.requireNonNull(who, "ComponentName is null");
+ Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)
+ || isProfileOwner(caller)
+ || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
+
+ Bundle restrictions = getUserRestrictionsFromPolicyEngine(admin, targetUserId);
+ // Add global restrictions set by the admin as well.
restrictions.putAll(
getUserRestrictionsFromPolicyEngine(admin, UserHandle.USER_ALL));
+ return restrictions;
+ } else {
+ if (!mInjector.isChangeEnabled(
+ ENABLE_COEXISTENCE_CHANGE, callerPackage, caller.getUserId())) {
+ throw new IllegalStateException("Calling package is not targeting Android U.");
+ }
+ return getUserRestrictionsFromPolicyEngine(admin, targetUserId);
}
- return restrictions;
} else {
Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
@@ -13439,164 +13471,162 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// Map of user restriction to permission.
- private static final HashMap<String, String> USER_RESTRICTION_PERMISSIONS = new HashMap<>();
+ private static final HashMap<String, String[]> USER_RESTRICTION_PERMISSIONS = new HashMap<>();
{
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.ENSURE_VERIFY_APPS, MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES);
+ UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, new String[]{MANAGE_DEVICE_POLICY_PROFILES});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_WIFI_TETHERING, MANAGE_DEVICE_POLICY_WIFI);
+ UserManager.DISALLOW_ADD_CLONE_PROFILE, new String[]{MANAGE_DEVICE_POLICY_PROFILES});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_WIFI_DIRECT, MANAGE_DEVICE_POLICY_WIFI);
+ UserManager.DISALLOW_ADD_USER, new String[]{MANAGE_DEVICE_POLICY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_USER_SWITCH, MANAGE_DEVICE_POLICY_USERS);
+ UserManager.DISALLOW_ADD_WIFI_CONFIG, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_USB_FILE_TRANSFER, MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER);
+ UserManager.DISALLOW_ADJUST_VOLUME, new String[]{MANAGE_DEVICE_POLICY_AUDIO_OUTPUT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_UNMUTE_MICROPHONE, MANAGE_DEVICE_POLICY_MICROPHONE);
+ UserManager.DISALLOW_AIRPLANE_MODE, new String[]{MANAGE_DEVICE_POLICY_AIRPLANE_MODE});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_UNMUTE_DEVICE, MANAGE_DEVICE_POLICY_AUDIO_OUTPUT);
+ UserManager.DISALLOW_AMBIENT_DISPLAY, new String[]{MANAGE_DEVICE_POLICY_DISPLAY});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_UNINSTALL_APPS, MANAGE_DEVICE_POLICY_APPS_CONTROL);
+ UserManager.DISALLOW_APPS_CONTROL, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_UNIFIED_PASSWORD, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS);
+ UserManager.DISALLOW_AUTOFILL, new String[]{MANAGE_DEVICE_POLICY_AUTOFILL});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS);
+ UserManager.DISALLOW_BLUETOOTH, new String[]{MANAGE_DEVICE_POLICY_BLUETOOTH});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SMS, MANAGE_DEVICE_POLICY_SMS);
+ UserManager.DISALLOW_BLUETOOTH_SHARING, new String[]{MANAGE_DEVICE_POLICY_BLUETOOTH});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, MANAGE_DEVICE_POLICY_WIFI);
+ UserManager.DISALLOW_CAMERA, new String[]{MANAGE_DEVICE_POLICY_CAMERA});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SHARE_LOCATION, MANAGE_DEVICE_POLICY_LOCATION);
+ UserManager.DISALLOW_CAMERA_TOGGLE, new String[]{MANAGE_DEVICE_POLICY_CAMERA_TOGGLE});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION);
+ UserManager.DISALLOW_CELLULAR_2G, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SET_WALLPAPER, MANAGE_DEVICE_POLICY_WALLPAPER);
+ UserManager.DISALLOW_CHANGE_WIFI_STATE, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SET_USER_ICON, MANAGE_DEVICE_POLICY_USERS);
+ UserManager.DISALLOW_CONFIG_BLUETOOTH, new String[]{MANAGE_DEVICE_POLICY_BLUETOOTH});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_SAFE_BOOT, MANAGE_DEVICE_POLICY_SAFE_BOOT);
+ UserManager.DISALLOW_CONFIG_BRIGHTNESS, new String[]{MANAGE_DEVICE_POLICY_DISPLAY});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_RUN_IN_BACKGROUND, MANAGE_DEVICE_POLICY_SAFE_BOOT);
+ UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_REMOVE_USER, MANAGE_DEVICE_POLICY_USERS);
+ UserManager.DISALLOW_CONFIG_CREDENTIALS, new String[]{MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_PRINTING, MANAGE_DEVICE_POLICY_PRINTING);
+ UserManager.DISALLOW_CONFIG_DATE_TIME, new String[]{MANAGE_DEVICE_POLICY_TIME});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_OUTGOING_CALLS, MANAGE_DEVICE_POLICY_CALLS);
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS, new String[]{MANAGE_DEFAULT_APPLICATIONS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_OUTGOING_BEAM, MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION);
+ UserManager.DISALLOW_CONFIG_LOCALE, new String[]{MANAGE_DEVICE_POLICY_LOCALE});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_NETWORK_RESET, MANAGE_DEVICE_POLICY_MOBILE_NETWORK);
+ UserManager.DISALLOW_CONFIG_LOCATION, new String[]{MANAGE_DEVICE_POLICY_LOCATION});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA);
+ UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_MODIFY_ACCOUNTS, MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT);
+ UserManager.DISALLOW_CONFIG_PRIVATE_DNS, new String[]{MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_MICROPHONE_TOGGLE, MANAGE_DEVICE_POLICY_MICROPHONE);
+ UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, new String[]{MANAGE_DEVICE_POLICY_DISPLAY});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
- MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES);
+ UserManager.DISALLOW_CONFIG_TETHERING, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
- MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES);
+ UserManager.DISALLOW_CONFIG_VPN, new String[]{MANAGE_DEVICE_POLICY_VPN});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_INSTALL_APPS, MANAGE_DEVICE_POLICY_APPS_CONTROL);
+ UserManager.DISALLOW_CONFIG_WIFI, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_FUN, MANAGE_DEVICE_POLICY_FUN);
+ UserManager.DISALLOW_CONTENT_CAPTURE, new String[]{MANAGE_DEVICE_POLICY_SCREEN_CONTENT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_FACTORY_RESET, MANAGE_DEVICE_POLICY_FACTORY_RESET);
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS, new String[]{MANAGE_DEVICE_POLICY_SCREEN_CONTENT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_DEBUGGING_FEATURES, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES);
+ UserManager.DISALLOW_CREATE_WINDOWS, new String[]{MANAGE_DEVICE_POLICY_WINDOWS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_DATA_ROAMING, MANAGE_DEVICE_POLICY_MOBILE_NETWORK);
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, new String[]{MANAGE_DEVICE_POLICY_PROFILE_INTERACTION});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION);
+ UserManager.DISALLOW_DATA_ROAMING, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CREATE_WINDOWS, MANAGE_DEVICE_POLICY_WINDOWS);
+ UserManager.DISALLOW_DEBUGGING_FEATURES, new String[]{MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONTENT_SUGGESTIONS, MANAGE_DEVICE_POLICY_SCREEN_CONTENT);
+ UserManager.DISALLOW_FACTORY_RESET, new String[]{MANAGE_DEVICE_POLICY_FACTORY_RESET});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONTENT_CAPTURE, MANAGE_DEVICE_POLICY_SCREEN_CONTENT);
+ UserManager.DISALLOW_FUN, new String[]{MANAGE_DEVICE_POLICY_FUN});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_WIFI, MANAGE_DEVICE_POLICY_WIFI);
+ UserManager.DISALLOW_INSTALL_APPS, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_VPN, MANAGE_DEVICE_POLICY_VPN);
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, new String[]{MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_TETHERING, MANAGE_DEVICE_POLICY_MOBILE_NETWORK);
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, new String[]{MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, MANAGE_DEVICE_POLICY_DISPLAY);
+ UserManager.DISALLOW_MICROPHONE_TOGGLE, new String[]{MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_PRIVATE_DNS, MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS);
+ UserManager.DISALLOW_MODIFY_ACCOUNTS, new String[]{MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, MANAGE_DEVICE_POLICY_MOBILE_NETWORK);
+ UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, new String[]{MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_LOCATION, MANAGE_DEVICE_POLICY_LOCATION);
+ UserManager.DISALLOW_NETWORK_RESET, new String[]{MANAGE_DEVICE_POLICY_MOBILE_NETWORK});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_LOCALE, MANAGE_DEVICE_POLICY_LOCALE);
+ UserManager.DISALLOW_OUTGOING_BEAM, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_DATE_TIME, MANAGE_DEVICE_POLICY_TIME);
+ UserManager.DISALLOW_OUTGOING_CALLS, new String[]{MANAGE_DEVICE_POLICY_CALLS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_CREDENTIALS, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS);
+ UserManager.DISALLOW_PRINTING, new String[]{MANAGE_DEVICE_POLICY_PRINTING});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, MANAGE_DEVICE_POLICY_MOBILE_NETWORK);
+ UserManager.DISALLOW_REMOVE_USER, new String[]{MANAGE_DEVICE_POLICY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_BRIGHTNESS, MANAGE_DEVICE_POLICY_DISPLAY);
+ UserManager.DISALLOW_RUN_IN_BACKGROUND, new String[]{MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_BLUETOOTH, MANAGE_DEVICE_POLICY_BLUETOOTH);
+ UserManager.DISALLOW_SAFE_BOOT, new String[]{MANAGE_DEVICE_POLICY_SAFE_BOOT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CHANGE_WIFI_STATE, MANAGE_DEVICE_POLICY_WIFI);
+ UserManager.DISALLOW_SET_USER_ICON, new String[]{MANAGE_DEVICE_POLICY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CAMERA_TOGGLE, MANAGE_DEVICE_POLICY_CAMERA);
+ UserManager.DISALLOW_SET_WALLPAPER, new String[]{MANAGE_DEVICE_POLICY_WALLPAPER});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CAMERA, MANAGE_DEVICE_POLICY_CAMERA);
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, new String[]{MANAGE_DEVICE_POLICY_PROFILE_INTERACTION});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_BLUETOOTH_SHARING, MANAGE_DEVICE_POLICY_BLUETOOTH);
+ UserManager.DISALLOW_SHARE_LOCATION, new String[]{MANAGE_DEVICE_POLICY_LOCATION});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_BLUETOOTH, MANAGE_DEVICE_POLICY_BLUETOOTH);
+ UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_BIOMETRIC, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS);
+ UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_AUTOFILL, MANAGE_DEVICE_POLICY_AUTOFILL);
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_APPS_CONTROL, MANAGE_DEVICE_POLICY_APPS_CONTROL);
+ UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_AMBIENT_DISPLAY, MANAGE_DEVICE_POLICY_DISPLAY);
+ UserManager.DISALLOW_UNIFIED_PASSWORD, new String[]{MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_AIRPLANE_MODE, MANAGE_DEVICE_POLICY_AIRPLANE_MODE);
+ UserManager.DISALLOW_UNINSTALL_APPS, new String[]{MANAGE_DEVICE_POLICY_APPS_CONTROL});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ADJUST_VOLUME, MANAGE_DEVICE_POLICY_AUDIO_OUTPUT);
+ UserManager.DISALLOW_UNMUTE_DEVICE, new String[]{MANAGE_DEVICE_POLICY_AUDIO_OUTPUT});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ADD_WIFI_CONFIG, MANAGE_DEVICE_POLICY_WIFI);
+ UserManager.DISALLOW_UNMUTE_MICROPHONE, new String[]{MANAGE_DEVICE_POLICY_MICROPHONE});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ADD_USER, MANAGE_DEVICE_POLICY_USERS);
+ UserManager.DISALLOW_USB_FILE_TRANSFER, new String[]{MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ADD_CLONE_PROFILE, MANAGE_DEVICE_POLICY_PROFILES);
+ UserManager.DISALLOW_USER_SWITCH, new String[]{MANAGE_DEVICE_POLICY_USERS});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, MANAGE_DEVICE_POLICY_PROFILES);
+ UserManager.DISALLOW_WIFI_DIRECT, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CELLULAR_2G, MANAGE_DEVICE_POLICY_MOBILE_NETWORK);
+ UserManager.DISALLOW_WIFI_TETHERING, new String[]{MANAGE_DEVICE_POLICY_WIFI});
USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
- MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION);
+ UserManager.ENSURE_VERIFY_APPS, new String[]{MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES});
// Restrictions not allowed to be set by admins.
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_RECORD_AUDIO, null);
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_WALLPAPER, null);
- USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_CONFIG_DEFAULT_APPS, null);
}
private EnforcingAdmin enforcePermissionForUserRestriction(ComponentName who,
String userRestriction, String callerPackageName, int userId) {
- String permission = USER_RESTRICTION_PERMISSIONS.get(userRestriction);
- if (permission != null) {
- return enforcePermissionAndGetEnforcingAdmin(who, permission, callerPackageName,
- userId);
- }
+ String[] permissions = USER_RESTRICTION_PERMISSIONS.get(userRestriction);
+ if (permissions.length > 0) {
+ try {
+ return enforcePermissionsAndGetEnforcingAdmin(who, permissions, callerPackageName,
+ userId);
+ } catch (SecurityException e) {
+ throw new SecurityException("Caller does not hold the required permission for this "
+ + "user restriction: " + userRestriction + ".\n" + e.getMessage());
+ }
+ }
throw new SecurityException("Admins are not permitted to set User Restriction: "
+ userRestriction);
}
@@ -14017,9 +14047,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
if (isPolicyEngineForFinanceFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
who,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ new String[]{
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL
+ },
caller.getPackageName(),
caller.getUserId());
mDevicePolicyEngine.setLocalPolicy(
@@ -16626,10 +16659,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
SENSOR_PERMISSIONS.add(Manifest.permission.BACKGROUND_CAMERA);
SENSOR_PERMISSIONS.add(Manifest.permission.RECORD_BACKGROUND_AUDIO);
SENSOR_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
- SENSOR_PERMISSIONS.add(
- Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE);
- SENSOR_PERMISSIONS.add(
- Manifest.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND);
}
private boolean canGrantPermission(CallerIdentity caller, String permission,
@@ -22422,6 +22451,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
});
}
+ // Permission that will need to be created in V.
+ private static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL =
+ "manage_device_policy_block_uninstall";
+ private static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE =
+ "manage_device_policy_camera_toggle";
+ private static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE =
+ "manage_device_policy_microphone_toggle";
+
// DPC types
private static final int DEFAULT_DEVICE_OWNER = 0;
private static final int FINANCED_DEVICE_OWNER = 1;
@@ -22645,7 +22682,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
{
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, DELEGATION_PERMISSION_GRANT);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APP_RESTRICTIONS, DELEGATION_APP_RESTRICTIONS);
- DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_APPS_CONTROL, DELEGATION_BLOCK_UNINSTALL);
+ DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL, DELEGATION_BLOCK_UNINSTALL);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_SECURITY_LOGGING, DELEGATION_SECURITY_LOGGING);
DELEGATE_SCOPES.put(MANAGE_DEVICE_POLICY_PACKAGE_STATE, DELEGATION_PACKAGE_ACCESS);
}
@@ -22724,6 +22761,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_AUTOFILL,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CAMERA_TOGGLE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
@@ -22740,6 +22781,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
+ CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL);
CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_PROFILES,
@@ -22770,13 +22813,34 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user. Only one permission provided in the list needs to be granted to pass this
+ * check.
+ * The given permissions will be checked along with their associated cross-user permissions if
+ * they exists and the target user is different to the calling user.
+ * Returns an {@link EnforcingAdmin} for the caller.
+ *
+ * @param admin the component name of the admin.
+ * @param callerPackageName The package name of the calling application.
+ * @param permissions an array of permission names to be checked.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private EnforcingAdmin enforcePermissionsAndGetEnforcingAdmin(@Nullable ComponentName admin,
+ String[] permissions, String callerPackageName, int targetUserId) {
+ enforcePermissions(permissions, callerPackageName, targetUserId);
+ return getEnforcingAdminForCaller(admin, callerPackageName);
+ }
+
+ /**
+ * Checks if the calling process has been granted permission to apply a device policy on a
* specific user.
* The given permission will be checked along with its associated cross-user permission if it
* exists and the target user is different to the calling user.
* Returns an {@link EnforcingAdmin} for the caller.
*
* @param admin the component name of the admin.
- * @param callerPackageName The package name of the calling application.
+ * @param callerPackageName The package name of the calling application.
* @param permission The name of the permission being checked.
* @param targetUserId The userId of the user which the caller needs permission to act on.
* @throws SecurityException if the caller has not been granted the given permission,
@@ -22834,6 +22898,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
new HashMap<>();
/**
+ * Checks if the calling process has been granted permission to apply a device policy.
+ *
+ * @param callerPackageName The package name of the calling application.
+ * @param permission The name of the permission being checked.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private void enforcePermission(String permission, String callerPackageName)
+ throws SecurityException {
+ if (!hasPermission(permission, callerPackageName)) {
+ throw new SecurityException("Caller does not have the required permissions for "
+ + "this user. Permission required: "
+ + permission
+ + ".");
+ }
+ }
+
+
+ /**
* Checks if the calling process has been granted permission to apply a device policy on a
* specific user.
* The given permission will be checked along with its associated cross-user permission if it
@@ -22847,17 +22930,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
*/
private void enforcePermission(String permission, String callerPackageName, int targetUserId)
throws SecurityException {
- if (!hasPermission(permission, callerPackageName, targetUserId)) {
- // TODO(b/276920002): Split the error messages so that the cross-user permission
- // is only mentioned when it is needed.
+ enforcePermission(permission, callerPackageName);
+ if (targetUserId != getCallerIdentity(callerPackageName).getUserId()) {
+ enforcePermission(CROSS_USER_PERMISSIONS.get(permission), callerPackageName);
+ }
+ }
+
+ /**
+ * Checks if the calling process has been granted permission to apply a device policy on a
+ * specific user. Only one of the given permissions will be required to be held to pass this
+ * check.
+ * The given permissions will be checked along with their associated cross-user permissions if
+ * they exist and the target user is different to the calling user.
+ *
+ * @param permissions An array of the names of the permissions being checked.
+ * @param callerPackageName The package name of the calling application.
+ * @param targetUserId The userId of the user which the caller needs permission to act on.
+ * @throws SecurityException if the caller has not been granted the given permission,
+ * the associated cross-user permission if the caller's user is different to the target user.
+ */
+ private void enforcePermissions(String[] permissions, String callerPackageName,
+ int targetUserId) throws SecurityException {
+ String heldPermission = "";
+ for (String permission : permissions) {
+ if (hasPermission(permission, callerPackageName)) {
+ heldPermission = permission;
+ break;
+ }
+ }
+ if (heldPermission.isEmpty()) {
throw new SecurityException("Caller does not have the required permissions for "
- + "this user. Permissions required: {"
- + permission
- + ", "
- + CROSS_USER_PERMISSIONS.get(permission)
- + "(if calling cross-user)"
- + "}");
+ + "this user. One of the following permission required: "
+ + Arrays.toString(permissions));
}
+ enforcePermission(heldPermission, callerPackageName, targetUserId);
}
/**
@@ -22867,25 +22973,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* exists and the target user is different to the calling user.
*
* @param callerPackageName The package name of the calling application.
+ * @param adminPolicy The admin policy that should grant holders permission.
* @param permission The name of the permission being checked.
* @param targetUserId The userId of the user which the caller needs permission to act on.
* @throws SecurityException if the caller has not been granted the given permission,
* the associated cross-user permission if the caller's user is different to the target user.
*/
private void enforcePermission(String permission, int adminPolicy,
- String callerPackageName, int targetUserId)
- throws SecurityException {
- if (!hasPermissionOrAdminPolicy(permission, callerPackageName, adminPolicy, targetUserId)) {
- // TODO(b/276920002): Split the error messages so that the cross-user permission
- // is only mentioned when it is needed.
- throw new SecurityException("Caller does not have the required permissions for "
- + "this user. Permissions required: {"
- + permission
- + ", "
- + CROSS_USER_PERMISSIONS.get(permission)
- + "(if calling cross-user)"
- + "}");
+ String callerPackageName, int targetUserId) throws SecurityException {
+ if (hasAdminPolicy(adminPolicy, callerPackageName)) {
+ return;
}
+ enforcePermission(permission, callerPackageName, targetUserId);
}
/**
@@ -22909,6 +23008,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforcePermission(permission, callerPackageName, targetUserId);
}
+ private boolean hasAdminPolicy(int adminPolicy, String callerPackageName) {
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller);
+ return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy);
+ }
+
/**
* Return whether the calling process has been granted permission to apply a device policy on
* a specific user.
@@ -22921,24 +23026,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
CallerIdentity caller = getCallerIdentity(callerPackageName);
boolean hasPermissionOnOwnUser = hasPermission(permission, caller.getPackageName());
boolean hasPermissionOnTargetUser = true;
- if (hasPermissionOnOwnUser & caller.getUserId() != targetUserId) {
- hasPermissionOnTargetUser = hasPermission(CROSS_USER_PERMISSIONS.get(permission),
- caller.getPackageName());
+ if (hasPermissionOnOwnUser && caller.getUserId() != targetUserId) {
+ hasPermissionOnTargetUser = hasPermissionOnTargetUser
+ && hasPermission(CROSS_USER_PERMISSIONS.get(permission),
+ caller.getPackageName());
}
return hasPermissionOnOwnUser && hasPermissionOnTargetUser;
}
- private boolean hasPermissionOrAdminPolicy(String permission, String callerPackageName,
- int adminPolicy, int targetUserId) {
- CallerIdentity caller = getCallerIdentity(callerPackageName);
- if (hasPermission(permission, caller.getPackageName(), targetUserId)) {
- return true;
- }
- ActiveAdmin deviceAdmin = getActiveAdminForCaller(null, caller);
- return deviceAdmin != null && deviceAdmin.info.usesPolicy(adminPolicy);
- }
-
/**
* Return whether the calling process has been granted the given permission.
*
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 509a66b4f8f7..8c2468af1146 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -284,6 +284,7 @@ final class PolicyDefinition<V> {
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
+ // TODO(b/277218360): Revisit policies that should be marked as global-only.
static {
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
@@ -312,8 +313,9 @@ final class PolicyDefinition<V> {
USER_RESTRICTION_FLAGS.put(
UserManager.DISALLOW_WIFI_TETHERING, POLICY_FLAG_GLOBAL_ONLY_POLICY);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_GRANT_ADMIN, /* flags= */ 0);
+ // TODO: set as global only once we get rid of the mapping
USER_RESTRICTION_FLAGS.put(
- UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(
UserManager.DISALLOW_WIFI_DIRECT, POLICY_FLAG_GLOBAL_ONLY_POLICY);
USER_RESTRICTION_FLAGS.put(
@@ -333,8 +335,10 @@ final class PolicyDefinition<V> {
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_BLUETOOTH, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH_SHARING, /* flags= */ 0);
+ // This effectively always applies globally, but it can be set on the profile
+ // parent, check the javadocs on the restriction for more info.
USER_RESTRICTION_FLAGS.put(
- UserManager.DISALLOW_USB_FILE_TRANSFER, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ UserManager.DISALLOW_USB_FILE_TRANSFER, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_CREDENTIALS, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_USER, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, /* flags= */ 0);
@@ -344,8 +348,10 @@ final class PolicyDefinition<V> {
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_DATE_TIME, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(
UserManager.DISALLOW_CONFIG_TETHERING, /* flags= */ 0);
+ // This effectively always applies globally, but it can be set on the profile
+ // parent, check the javadocs on the restriction for more info.
USER_RESTRICTION_FLAGS.put(
- UserManager.DISALLOW_NETWORK_RESET, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ UserManager.DISALLOW_NETWORK_RESET, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_FACTORY_RESET, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_USER, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_MANAGED_PROFILE, /* flags= */ 0);
@@ -376,8 +382,7 @@ final class PolicyDefinition<V> {
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNMUTE_DEVICE, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_DATA_ROAMING, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SET_USER_ICON, /* flags= */ 0);
- // TODO: double check flags
- USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OEM_UNLOCK, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+ USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OEM_UNLOCK, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNIFIED_PASSWORD, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, /* flags= */ 0);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_AUTOFILL, /* flags= */ 0);
@@ -390,6 +395,7 @@ final class PolicyDefinition<V> {
USER_RESTRICTION_FLAGS.put(
UserManager.DISALLOW_CONFIG_PRIVATE_DNS, POLICY_FLAG_GLOBAL_ONLY_POLICY);
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MICROPHONE_TOGGLE, /* flags= */ 0);
+ // TODO: According the UserRestrictionsUtils, this is global only, need to confirm.
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CAMERA_TOGGLE, /* flags= */ 0);
// TODO: check if its global only
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BIOMETRIC, /* flags= */ 0);
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 1a7517098d18..7b771aff0055 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -567,36 +567,36 @@ public class RescuePartyTest {
// Ensure that no action is taken for cases where the failure reason is unknown
assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1),
- PackageHealthObserverImpact.USER_IMPACT_NONE);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
// Ensure the correct user impact is returned for each mitigation count.
assertEquals(observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
- PackageHealthObserverImpact.USER_IMPACT_LOW);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
assertEquals(observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
- PackageHealthObserverImpact.USER_IMPACT_LOW);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
assertEquals(observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3),
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
assertEquals(observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4),
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
}
@Test
public void testBootLoopLevels() {
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_NONE);
- assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LOW);
- assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LOW);
- assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_HIGH);
- assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_HIGH);
- assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
+ assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
+ assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
+ assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+ assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+ assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 8211d6fc03a2..bc3f5ab5c42b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -237,9 +237,23 @@ public final class BroadcastQueueModernImplTest {
}
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
+ BroadcastRecord record, int recordIndex) {
+ enqueueOrReplaceBroadcast(queue, record, recordIndex, false, 42_000_000L);
+ }
+
+ private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
BroadcastRecord record, int recordIndex, long enqueueTime) {
- queue.enqueueOrReplaceBroadcast(record, recordIndex, false);
+ enqueueOrReplaceBroadcast(queue, record, recordIndex, false, enqueueTime);
+ }
+
+ private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
+ BroadcastRecord record, int recordIndex, boolean wouldBeSkipped, long enqueueTime) {
+ queue.enqueueOrReplaceBroadcast(record, recordIndex, wouldBeSkipped, (r, i) -> {
+ throw new UnsupportedOperationException();
+ });
record.enqueueTime = enqueueTime;
+ record.enqueueRealTime = enqueueTime;
+ record.enqueueClockTime = enqueueTime;
}
@Test
@@ -370,7 +384,7 @@ public final class BroadcastQueueModernImplTest {
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, options,
List.of(makeMockRegisteredReceiver()), false);
- queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
queue.setProcessAndUidCached(null, false);
final long notCachedRunnableAt = queue.getRunnableAt();
@@ -397,7 +411,7 @@ public final class BroadcastQueueModernImplTest {
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, options,
List.of(makeMockRegisteredReceiver()), false);
- queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
queue.setProcessAndUidCached(null, false);
final long notCachedRunnableAt = queue.getRunnableAt();
@@ -421,12 +435,12 @@ public final class BroadcastQueueModernImplTest {
// enqueue a bg-priority broadcast then a fg-priority one
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
final BroadcastRecord timezoneRecord = makeBroadcastRecord(timezone);
- queue.enqueueOrReplaceBroadcast(timezoneRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, timezoneRecord, 0);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
- queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
// verify that:
// (a) the queue is immediately runnable by existence of a fg-priority broadcast
@@ -457,13 +471,14 @@ public final class BroadcastQueueModernImplTest {
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, null,
List.of(withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 0)), true);
- queue.enqueueOrReplaceBroadcast(airplaneRecord, 1, false);
+ enqueueOrReplaceBroadcast(queue, airplaneRecord, 1);
assertFalse(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
// Bumping past barrier makes us now runnable
- airplaneRecord.terminalCount++;
+ airplaneRecord.setDeliveryState(0, BroadcastRecord.DELIVERY_DELIVERED,
+ "testRunnableAt_Ordered");
queue.invalidateRunnableAt();
assertTrue(queue.isRunnable());
assertNotEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
@@ -480,7 +495,7 @@ public final class BroadcastQueueModernImplTest {
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
List.of(makeMockRegisteredReceiver()));
- queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
mConstants.MAX_PENDING_BROADCASTS = 128;
queue.invalidateRunnableAt();
@@ -506,11 +521,11 @@ public final class BroadcastQueueModernImplTest {
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
List.of(makeMockRegisteredReceiver()));
- queue.enqueueOrReplaceBroadcast(lazyRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, lazyRecord, 0);
assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime);
assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason);
- queue.enqueueOrReplaceBroadcast(testRecord, 0, false);
+ enqueueOrReplaceBroadcast(queue, testRecord, 0);
assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime);
assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason);
}
@@ -572,22 +587,22 @@ public final class BroadcastQueueModernImplTest {
BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
- queue.enqueueOrReplaceBroadcast(
+ enqueueOrReplaceBroadcast(queue,
makeBroadcastRecord(new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
- .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, false);
- queue.enqueueOrReplaceBroadcast(
- makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0, false);
- queue.enqueueOrReplaceBroadcast(
+ .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0);
+ enqueueOrReplaceBroadcast(queue,
+ makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0);
+ enqueueOrReplaceBroadcast(queue,
makeBroadcastRecord(new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, false);
- queue.enqueueOrReplaceBroadcast(
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0);
+ enqueueOrReplaceBroadcast(queue,
makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED)
- .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, false);
- queue.enqueueOrReplaceBroadcast(
- makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0, false);
- queue.enqueueOrReplaceBroadcast(
+ .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0);
+ enqueueOrReplaceBroadcast(queue,
+ makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0);
+ enqueueOrReplaceBroadcast(queue,
makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, false);
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0);
queue.makeActiveNextPending();
assertEquals(Intent.ACTION_LOCKED_BOOT_COMPLETED, queue.getActive().intent.getAction());
@@ -1163,8 +1178,8 @@ public final class BroadcastQueueModernImplTest {
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(timeTick,
- List.of(makeMockRegisteredReceiver())), 0, false);
+ enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(timeTick,
+ List.of(makeMockRegisteredReceiver())), 0);
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
// Make the foreground broadcast as active.
@@ -1175,15 +1190,15 @@ public final class BroadcastQueueModernImplTest {
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(airplane,
- List.of(makeMockRegisteredReceiver())), 0, false);
+ enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(airplane,
+ List.of(makeMockRegisteredReceiver())), 0);
// Make the background broadcast as active.
queue.makeActiveNextPending();
assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
- queue.enqueueOrReplaceBroadcast(makeBroadcastRecord(timeTick,
- List.of(makeMockRegisteredReceiver())), 0, false);
+ enqueueOrReplaceBroadcast(queue, makeBroadcastRecord(timeTick,
+ List.of(makeMockRegisteredReceiver())), 0);
// Even though the active broadcast is not a foreground one, scheduling group will be
// DEFAULT since there is a foreground broadcast waiting to be delivered.
assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 64a95ca843d3..90e6a2d0f34a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -464,7 +464,7 @@ public class BroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleReceiver() for "
- + Arrays.toString(invocation.getArguments()));
+ + Arrays.toString(invocation.getArguments()) + " package " + ai.packageName);
assertHealth();
final Intent intent = invocation.getArgument(0);
final Bundle extras = invocation.getArgument(5);
@@ -486,7 +486,7 @@ public class BroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
- + Arrays.toString(invocation.getArguments()));
+ + Arrays.toString(invocation.getArguments()) + " package " + ai.packageName);
assertHealth();
final Intent intent = invocation.getArgument(1);
final Bundle extras = invocation.getArgument(4);
@@ -1981,6 +1981,46 @@ public class BroadcastQueueTest {
}
/**
+ * Confirm how many times a pathological broadcast pattern results in OOM
+ * adjusts; watches for performance regressions.
+ */
+ @Test
+ public void testOomAdjust_TriggerCount() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ // Send 8 broadcasts, 4 receivers in the first process,
+ // and 2 alternating in each of the remaining processes
+ synchronized (mAms) {
+ for (int i = 0; i < 8; i++) {
+ final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+ }
+ }
+ waitForIdle();
+
+ final int expectedTimes;
+ switch (mImpl) {
+ // Original stack requested for every single receiver; yikes
+ case DEFAULT: expectedTimes = 64; break;
+ // Modern stack requests once each time we promote a process to
+ // running; we promote "green" twice, and "blue" and "yellow" once
+ case MODERN: expectedTimes = 4; break;
+ default: throw new UnsupportedOperationException();
+ }
+
+ verify(mAms, times(expectedTimes))
+ .updateOomAdjPendingTargetsLocked(eq(OOM_ADJ_REASON_START_RECEIVER));
+ }
+
+ /**
* Verify that expected events are triggered when a broadcast is finished.
*/
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index 2b6f2174d49b..08952eab071f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -24,7 +24,12 @@ import static android.content.Intent.ACTION_TIME_CHANGED;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
-import static com.android.server.am.BroadcastRecord.calculateBlockedUntilTerminalCount;
+import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED;
+import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED;
+import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING;
+import static com.android.server.am.BroadcastRecord.DELIVERY_SKIPPED;
+import static com.android.server.am.BroadcastRecord.DELIVERY_TIMEOUT;
+import static com.android.server.am.BroadcastRecord.calculateBlockedUntilBeyondCount;
import static com.android.server.am.BroadcastRecord.calculateDeferUntilActive;
import static com.android.server.am.BroadcastRecord.calculateUrgent;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
@@ -58,7 +63,6 @@ import androidx.test.filters.SmallTest;
import com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -79,6 +83,7 @@ import java.util.function.BiFunction;
@SmallTest
@RunWith(MockitoJUnitRunner.class)
public class BroadcastRecordTest {
+ private static final String TAG = "BroadcastRecordTest";
private static final int USER0 = UserHandle.USER_SYSTEM;
private static final int USER1 = USER0 + 1;
@@ -120,13 +125,13 @@ public class BroadcastRecordTest {
assertFalse(isPrioritized(List.of(createResolveInfo(PACKAGE1, getAppId(1), 10))));
assertArrayEquals(new int[] {-1},
- calculateBlockedUntilTerminalCount(List.of(
+ calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 0)), false));
assertArrayEquals(new int[] {-1},
- calculateBlockedUntilTerminalCount(List.of(
+ calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), -10)), false));
assertArrayEquals(new int[] {-1},
- calculateBlockedUntilTerminalCount(List.of(
+ calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10)), false));
}
@@ -142,12 +147,12 @@ public class BroadcastRecordTest {
createResolveInfo(PACKAGE3, getAppId(3), 10))));
assertArrayEquals(new int[] {-1,-1,-1},
- calculateBlockedUntilTerminalCount(List.of(
+ calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 0),
createResolveInfo(PACKAGE2, getAppId(2), 0),
createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
assertArrayEquals(new int[] {-1,-1,-1},
- calculateBlockedUntilTerminalCount(List.of(
+ calculateBlockedUntilBeyondCount(List.of(
createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 10),
createResolveInfo(PACKAGE3, getAppId(3), 10)), false));
@@ -156,26 +161,176 @@ public class BroadcastRecordTest {
@Test
public void testIsPrioritized_Yes() {
assertTrue(isPrioritized(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), -10),
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
- createResolveInfo(PACKAGE3, getAppId(3), 10))));
+ createResolveInfo(PACKAGE3, getAppId(3), -10))));
assertTrue(isPrioritized(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), 0),
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
- createResolveInfo(PACKAGE3, getAppId(3), 10))));
+ createResolveInfo(PACKAGE3, getAppId(3), 0))));
assertArrayEquals(new int[] {0,1,2},
- calculateBlockedUntilTerminalCount(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), -10),
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 10),
createResolveInfo(PACKAGE2, getAppId(2), 0),
- createResolveInfo(PACKAGE3, getAppId(3), 10)), false));
+ createResolveInfo(PACKAGE3, getAppId(3), -10)), false));
assertArrayEquals(new int[] {0,0,2,3,3},
- calculateBlockedUntilTerminalCount(List.of(
- createResolveInfo(PACKAGE1, getAppId(1), 0),
- createResolveInfo(PACKAGE2, getAppId(2), 0),
+ calculateBlockedUntilBeyondCount(List.of(
+ createResolveInfo(PACKAGE1, getAppId(1), 20),
+ createResolveInfo(PACKAGE2, getAppId(2), 20),
createResolveInfo(PACKAGE3, getAppId(3), 10),
- createResolveInfo(PACKAGE3, getAppId(3), 20),
- createResolveInfo(PACKAGE3, getAppId(3), 20)), false));
+ createResolveInfo(PACKAGE3, getAppId(3), 0),
+ createResolveInfo(PACKAGE3, getAppId(3), 0)), false));
+ }
+
+ @Test
+ public void testSetDeliveryState_Single() {
+ final BroadcastRecord r = createBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
+ createResolveInfoWithPriority(0)));
+ assertEquals(DELIVERY_PENDING, r.getDeliveryState(0));
+ assertBlocked(r, false);
+ assertTerminalDeferredBeyond(r, 0, 0, 0);
+
+ r.setDeliveryState(0, DELIVERY_DEFERRED, TAG);
+ assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0));
+ assertBlocked(r, false);
+ assertTerminalDeferredBeyond(r, 0, 1, 1);
+
+ // Identical state change has no effect
+ r.setDeliveryState(0, DELIVERY_DEFERRED, TAG);
+ assertEquals(DELIVERY_DEFERRED, r.getDeliveryState(0));
+ assertBlocked(r, false);
+ assertTerminalDeferredBeyond(r, 0, 1, 1);
+
+ // Moving to terminal state updates counters
+ r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
+ assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0));
+ assertBlocked(r, false);
+ assertTerminalDeferredBeyond(r, 1, 0, 1);
+
+ // Trying to change terminal state has no effect
+ r.setDeliveryState(0, DELIVERY_TIMEOUT, TAG);
+ assertEquals(DELIVERY_DELIVERED, r.getDeliveryState(0));
+ assertBlocked(r, false);
+ assertTerminalDeferredBeyond(r, 1, 0, 1);
+ }
+
+ @Test
+ public void testSetDeliveryState_Unordered() {
+ final BroadcastRecord r = createBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0)));
+ assertBlocked(r, false, false, false);
+ assertTerminalDeferredBeyond(r, 0, 0, 0);
+
+ // Even though we finish a middle item in the tranche, we're not
+ // "beyond" it because there is still unfinished work before it
+ r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false);
+ assertTerminalDeferredBeyond(r, 1, 0, 0);
+
+ r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 0, 2);
+
+ r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false);
+ assertTerminalDeferredBeyond(r, 3, 0, 3);
+ }
+
+ @Test
+ public void testSetDeliveryState_Ordered() {
+ final BroadcastRecord r = createOrderedBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0)));
+ assertBlocked(r, false, true, true);
+ assertTerminalDeferredBeyond(r, 0, 0, 0);
+
+ r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, true);
+ assertTerminalDeferredBeyond(r, 1, 0, 1);
+
+ r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 0, 2);
+
+ r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false);
+ assertTerminalDeferredBeyond(r, 3, 0, 3);
+ }
+
+ @Test
+ public void testSetDeliveryState_DeferUntilActive() {
+ final BroadcastRecord r = createBroadcastRecord(
+ new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of(
+ createResolveInfoWithPriority(10),
+ createResolveInfoWithPriority(10),
+ createResolveInfoWithPriority(10),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(0),
+ createResolveInfoWithPriority(-10),
+ createResolveInfoWithPriority(-10),
+ createResolveInfoWithPriority(-10)));
+ assertBlocked(r, false, false, false, true, true, true, true, true, true);
+ assertTerminalDeferredBeyond(r, 0, 0, 0);
+
+ r.setDeliveryState(0, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(1, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(2, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(3, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(4, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(5, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(6, DELIVERY_DEFERRED, TAG);
+ r.setDeliveryState(7, DELIVERY_PENDING, TAG);
+ r.setDeliveryState(8, DELIVERY_DEFERRED, TAG);
+
+ // Verify deferred counts ratchet up, but we're not "beyond" the first
+ // still-pending receiver
+ assertBlocked(r, false, false, false, true, true, true, true, true, true);
+ assertTerminalDeferredBeyond(r, 0, 6, 0);
+
+ // We're still not "beyond" the first still-pending receiver, even when
+ // we finish a receiver later in the first tranche
+ r.setDeliveryState(2, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, true, true, true, true, true, true);
+ assertTerminalDeferredBeyond(r, 1, 6, 0);
+
+ // Completing that last item in first tranche means we now unblock the
+ // second tranche, and since it's entirely deferred, the third traunche
+ // is unblocked too
+ r.setDeliveryState(0, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 6, 7);
+
+ // Moving a deferred item in an earlier tranche back to being pending
+ // doesn't change the fact that we've already moved beyond it
+ r.setDeliveryState(1, DELIVERY_PENDING, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 2, 5, 7);
+ r.setDeliveryState(1, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 3, 5, 7);
+
+ // Completing middle pending item is enough to fast-forward to end
+ r.setDeliveryState(7, DELIVERY_DELIVERED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 4, 5, 9);
+
+ // Moving everyone else directly into a finished state updates all the
+ // terminal counters
+ r.setDeliveryState(3, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(4, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(5, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(6, DELIVERY_SKIPPED, TAG);
+ r.setDeliveryState(8, DELIVERY_SKIPPED, TAG);
+ assertBlocked(r, false, false, false, false, false, false, false, false, false);
+ assertTerminalDeferredBeyond(r, 9, 0, 9);
}
@Test
@@ -688,6 +843,10 @@ public class BroadcastRecordTest {
: errorMsg.insert(0, "Contains unexpected receiver: ").toString();
}
+ private static ResolveInfo createResolveInfoWithPriority(int priority) {
+ return createResolveInfo(PACKAGE1, getAppId(1), priority);
+ }
+
private static ResolveInfo createResolveInfo(String packageName, int uid) {
return createResolveInfo(packageName, uid, 0);
}
@@ -738,21 +897,40 @@ public class BroadcastRecordTest {
return excludedList;
}
+ private BroadcastRecord createBroadcastRecord(Intent intent,
+ List<ResolveInfo> receivers) {
+ return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */,
+ null /* options */, false);
+ }
+
+ private BroadcastRecord createOrderedBroadcastRecord(Intent intent,
+ List<ResolveInfo> receivers) {
+ return createBroadcastRecord(receivers, USER0, intent, null /* filterExtrasForReceiver */,
+ null /* options */, true);
+ }
+
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent) {
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
- null /* options */);
+ null /* options */, false);
}
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent, BroadcastOptions options) {
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
- options);
+ options, false);
}
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
BroadcastOptions options) {
+ return createBroadcastRecord(receivers, userId, intent, filterExtrasForReceiver,
+ options, false);
+ }
+
+ private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
+ Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+ BroadcastOptions options, boolean ordered) {
return new BroadcastRecord(
mQueue /* queue */,
intent,
@@ -774,7 +952,7 @@ public class BroadcastRecordTest {
0 /* resultCode */,
null /* resultData */,
null /* resultExtras */,
- false /* serialized */,
+ ordered /* serialized */,
false /* sticky */,
false /* initialSticky */,
userId,
@@ -789,6 +967,20 @@ public class BroadcastRecordTest {
private static boolean isPrioritized(List<Object> receivers) {
return BroadcastRecord.isPrioritized(
- calculateBlockedUntilTerminalCount(receivers, false), false);
+ calculateBlockedUntilBeyondCount(receivers, false), false);
+ }
+
+ private static void assertBlocked(BroadcastRecord r, boolean... blocked) {
+ assertEquals(r.receivers.size(), blocked.length);
+ for (int i = 0; i < blocked.length; i++) {
+ assertEquals("blocked " + i, blocked[i], r.isBlocked(i));
+ }
+ }
+
+ private static void assertTerminalDeferredBeyond(BroadcastRecord r,
+ int expectedTerminalCount, int expectedDeferredCount, int expectedBeyondCount) {
+ assertEquals("terminal", expectedTerminalCount, r.terminalCount);
+ assertEquals("deferred", expectedDeferredCount, r.deferredCount);
+ assertEquals("beyond", expectedBeyondCount, r.beyondCount);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index 0be678af12dc..541b07782b29 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -16,31 +16,65 @@
package com.android.server.rollback;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
import android.util.Log;
import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.PackageWatchdog;
import com.android.server.SystemConfig;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import org.xmlpull.v1.XmlPullParser;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.List;
import java.util.Scanner;
+
@RunWith(AndroidJUnit4.class)
public class RollbackPackageHealthObserverTest {
+ @Mock
+ private Context mMockContext;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PackageWatchdog mMockPackageWatchdog;
+ @Mock
+ RollbackManager mRollbackManager;
+ @Mock
+ RollbackInfo mRollbackInfo;
+ @Mock
+ PackageRollbackInfo mPackageRollbackInfo;
+
+ private MockitoSession mSession;
+ private static final String APP_A = "com.package.a";
+ private static final long VERSION_CODE = 1L;
private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
private SystemConfig mSysConfig;
@@ -50,17 +84,74 @@ public class RollbackPackageHealthObserverTest {
@Before
public void setup() {
mSysConfig = new SystemConfigTestClass();
+
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(PackageWatchdog.class)
+ .startMocking();
+
+ // Mock PackageWatchdog
+ doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
+ .when(() -> PackageWatchdog.getInstance(mMockContext));
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSession.finishMocking();
}
/**
- * Subclass of SystemConfig without running the constructor.
- */
+ * Subclass of SystemConfig without running the constructor.
+ */
private class SystemConfigTestClass extends SystemConfig {
SystemConfigTestClass() {
- super(false);
+ super(false);
}
}
+ @Test
+ public void testHealthCheckLevels() {
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext));
+ VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE);
+
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+
+ // Crashes with no rollbacks available
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
+
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
+ when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
+ when(mPackageRollbackInfo.getVersionRolledBackFrom()).thenReturn(testFailedPackage);
+
+ // native crash
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+ observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
+ // non-native crash
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+ observer.onHealthCheckFailed(testFailedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
+ // Second non-native crash again
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ observer.onHealthCheckFailed(testFailedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 2));
+ // Subsequent crashes when rollbacks have completed
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onHealthCheckFailed(testFailedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 3));
+ }
+
/**
* Test that isAutomaticRollbackDenied works correctly when packages that are not
* denied are sent.
@@ -77,7 +168,7 @@ public class RollbackPackageHealthObserverTest {
readPermissions(folder, /* Grant all permission flags */ ~0);
assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
- new VersionedPackage("com.test.package", 1))).isEqualTo(false);
+ new VersionedPackage("com.test.package", 1))).isEqualTo(false);
}
/**
@@ -96,7 +187,7 @@ public class RollbackPackageHealthObserverTest {
readPermissions(folder, /* Grant all permission flags */ ~0);
assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
- new VersionedPackage("com.android.vending", 1))).isEqualTo(true);
+ new VersionedPackage("com.android.vending", 1))).isEqualTo(true);
}
/**
@@ -109,7 +200,7 @@ public class RollbackPackageHealthObserverTest {
readPermissions(folder, /* Grant all permission flags */ ~0);
assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
- new VersionedPackage("com.android.vending", 1))).isEqualTo(false);
+ new VersionedPackage("com.android.vending", 1))).isEqualTo(false);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 7468901a16af..d5d06d3b4eb8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.face.aidl;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -148,6 +150,28 @@ public class FaceAuthenticationClientTest {
}
@Test
+ public void testLockoutEndsOperation() throws RemoteException {
+ final FaceAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+ client.onLockoutPermanent();
+
+ verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(),
+ eq(FACE_ERROR_LOCKOUT_PERMANENT), anyInt());
+ verify(mCallback).onClientFinished(client, false);
+ }
+
+ @Test
+ public void testTemporaryLockoutEndsOperation() throws RemoteException {
+ final FaceAuthenticationClient client = createClient(2);
+ client.start(mCallback);
+ client.onLockoutTimed(1000);
+
+ verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(),
+ eq(FACE_ERROR_LOCKOUT), anyInt());
+ verify(mCallback).onClientFinished(client, false);
+ }
+
+ @Test
public void notifyHalWhenContextChanges() throws RemoteException {
final FaceAuthenticationClient client = createClient();
client.start(mCallback);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index ff6c9769b69f..516fb4aa40c6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -15,23 +15,31 @@
*/
package com.android.server.notification;
-import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
+import static android.app.Notification.FLAG_AUTO_CANCEL;
+import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_CAN_COLORIZE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_NO_CLEAR;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+
+import static com.android.server.notification.GroupHelper.BASE_FLAGS;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import android.annotation.SuppressLint;
import android.app.Notification;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
@@ -45,11 +53,10 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Map;
@SmallTest
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the class.
@RunWith(AndroidJUnit4.class)
public class GroupHelperTest extends UiServiceTestCase {
private @Mock GroupHelper.Callback mCallback;
@@ -82,21 +89,104 @@ public class GroupHelperTest extends UiServiceTestCase {
}
@Test
- public void testNoGroup_postingUnderLimit() throws Exception {
+ public void testGetAutogroupSummaryFlags_noChildren() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+
+ assertEquals(BASE_FLAGS, mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_oneOngoing() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", 0);
+ children.put("b", FLAG_ONGOING_EVENT);
+ children.put("c", FLAG_BUBBLE);
+
+ assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_oneOngoingNoClear() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", 0);
+ children.put("b", FLAG_ONGOING_EVENT|FLAG_NO_CLEAR);
+ children.put("c", FLAG_BUBBLE);
+
+ assertEquals(FLAG_NO_CLEAR | FLAG_ONGOING_EVENT | BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_oneOngoingBubble() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", 0);
+ children.put("b", FLAG_ONGOING_EVENT | FLAG_BUBBLE);
+ children.put("c", FLAG_BUBBLE);
+
+ assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_multipleOngoing() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", 0);
+ children.put("b", FLAG_ONGOING_EVENT);
+ children.put("c", FLAG_BUBBLE);
+ children.put("d", FLAG_ONGOING_EVENT);
+
+ assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_oneAutoCancel() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", 0);
+ children.put("b", FLAG_AUTO_CANCEL);
+ children.put("c", FLAG_BUBBLE);
+
+ assertEquals(BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_allAutoCancel() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", FLAG_AUTO_CANCEL);
+ children.put("b", FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE);
+ children.put("c", FLAG_AUTO_CANCEL);
+ children.put("d", FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE);
+
+ assertEquals(FLAG_AUTO_CANCEL | BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testGetAutogroupSummaryFlags_allAutoCancelOneOngoing() {
+ ArrayMap<String, Integer> children = new ArrayMap<>();
+ children.put("a", FLAG_AUTO_CANCEL);
+ children.put("b", FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE);
+ children.put("c", FLAG_AUTO_CANCEL);
+ children.put("d", FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE | FLAG_ONGOING_EVENT);
+
+ assertEquals(FLAG_AUTO_CANCEL| FLAG_ONGOING_EVENT | BASE_FLAGS,
+ mGroupHelper.getAutogroupSummaryFlags(children));
+ }
+
+ @Test
+ public void testNoGroup_postingUnderLimit() {
final String pkg = "package";
for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
- verify(mCallback, never()).addAutoGroupSummary(
- eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean());
- verify(mCallback, never()).addAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verifyZeroInteractions(mCallback);
}
@Test
- public void testNoGroup_multiPackage() throws Exception {
+ public void testNoGroup_multiPackage() {
final String pkg = "package";
final String pkg2 = "package2";
for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
@@ -105,31 +195,23 @@ public class GroupHelperTest extends UiServiceTestCase {
}
mGroupHelper.onNotificationPosted(
getSbn(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
- verify(mCallback, never()).addAutoGroupSummary(
- eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean());
- verify(mCallback, never()).addAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verifyZeroInteractions(mCallback);
}
@Test
- public void testNoGroup_multiUser() throws Exception {
+ public void testNoGroup_multiUser() {
final String pkg = "package";
for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
mGroupHelper.onNotificationPosted(
- getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.ALL), false);
- verify(mCallback, never()).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), anyBoolean());
- verify(mCallback, never()).addAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
+ verifyZeroInteractions(mCallback);
}
@Test
- public void testNoGroup_someAreGrouped() throws Exception {
+ public void testNoGroup_someAreGrouped() {
final String pkg = "package";
for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(
@@ -137,233 +219,344 @@ public class GroupHelperTest extends UiServiceTestCase {
}
mGroupHelper.onNotificationPosted(
getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"), false);
- verify(mCallback, never()).addAutoGroupSummary(
- eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean());
- verify(mCallback, never()).addAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verifyZeroInteractions(mCallback);
}
@Test
- public void testPostingOverLimit() throws Exception {
+ public void testAddSummary() {
final String pkg = "package";
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
mGroupHelper.onNotificationPosted(
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false));
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
}
@Test
- public void testPostingOverLimit_addsOngoingFlag() throws Exception {
+ public void testAddSummary_oneChildOngoing_summaryOngoing() {
final String pkg = "package";
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
if (i == 0) {
- sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
}
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(true));
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
}
@Test
- public void testAutoGroupCount_addingNoGroupSBN() {
+ public void testAddSummary_oneChildAutoCancel_summaryNotAutoCancel() {
final String pkg = "package";
- ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ if (i == 0) {
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ }
+ mGroupHelper.onNotificationPosted(sbn, false);
}
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ }
- for (StatusBarNotification sbn: notifications) {
- sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ @Test
+ public void testAddSummary_allChildrenAutoCancel_summaryAutoCancel() {
+ final String pkg = "package";
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ mGroupHelper.onNotificationPosted(sbn, false);
}
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(BASE_FLAGS | FLAG_AUTO_CANCEL));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ }
- for (StatusBarNotification sbn: notifications) {
- mGroupHelper.onNotificationPosted(sbn, true);
+ @Test
+ public void testAddSummary_summaryAutoCancelNoClear() {
+ final String pkg = "package";
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ if (i == 0) {
+ sbn.getNotification().flags |= FLAG_NO_CLEAR;
+ }
+ mGroupHelper.onNotificationPosted(sbn, false);
}
-
- verify(mCallback, times(AUTOGROUP_AT_COUNT + 1))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
-
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg), AUTOGROUP_AT_COUNT + 1);
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+ eq(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
}
@Test
- public void testAutoGroupCount_UpdateNotification() {
+ public void testAutoGrouped_allOngoing_updateChildNotOngoing() {
final String pkg = "package";
- ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
- }
- for (StatusBarNotification sbn: notifications) {
- sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ notifications.add(sbn);
}
for (StatusBarNotification sbn: notifications) {
- mGroupHelper.onNotificationPosted(sbn, true);
+ mGroupHelper.onNotificationPosted(sbn, false);
}
- notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT;
- mGroupHelper.onNotificationUpdated(notifications.get(0));
+ // One notification is no longer ongoing
+ notifications.get(0).getNotification().flags &= ~FLAG_ONGOING_EVENT;
+ mGroupHelper.onNotificationPosted(notifications.get(0), true);
- verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
-
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg), AUTOGROUP_AT_COUNT);
+ // Summary should keep FLAG_ONGOING_EVENT if any child has it
+ verify(mCallback).updateAutogroupSummary(
+ anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
}
@Test
- public void testAutoGroupCount_UpdateNotificationAfterChanges() {
+ public void testAutoGrouped_singleOngoing_removeOngoingChild() {
final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ if (i == 0) {
+ sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ }
+ notifications.add(sbn);
}
for (StatusBarNotification sbn: notifications) {
- sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ mGroupHelper.onNotificationPosted(sbn, false);
}
- for (StatusBarNotification sbn: notifications) {
- mGroupHelper.onNotificationPosted(sbn, true);
- }
+ // remove ongoing
+ mGroupHelper.onNotificationRemoved(notifications.get(0));
- notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT;
+ // Summary is no longer ongoing
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
+ }
- mGroupHelper.onNotificationUpdated(notifications.get(0));
+ @Test
+ public void testAutoGrouped_noOngoing_updateOngoingChild() {
+ final String pkg = "package";
- notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ notifications.add(sbn);
+ }
- mGroupHelper.onNotificationUpdated(notifications.get(0));
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
- verify(mCallback, times(AUTOGROUP_AT_COUNT + 3))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
+ // update to ongoing
+ notifications.get(0).getNotification().flags |= FLAG_ONGOING_EVENT;
+ mGroupHelper.onNotificationPosted(notifications.get(0), true);
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg), AUTOGROUP_AT_COUNT + 1);
+ // Summary is now ongoing
+ verify(mCallback).updateAutogroupSummary(
+ anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
}
@Test
- public void testAutoGroupCount_RemoveNotification() {
+ public void testAutoGrouped_noOngoing_addOngoingChild() {
final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ notifications.add(sbn);
}
for (StatusBarNotification sbn: notifications) {
- sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ mGroupHelper.onNotificationPosted(sbn, false);
}
- for (StatusBarNotification sbn: notifications) {
- mGroupHelper.onNotificationPosted(sbn, true);
+ // add ongoing
+ StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT + 1, null, UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ mGroupHelper.onNotificationPosted(sbn, true);
+
+ // Summary is now ongoing
+ verify(mCallback).updateAutogroupSummary(
+ anyInt(), anyString(), eq(BASE_FLAGS | FLAG_ONGOING_EVENT));
+ }
+
+ @Test
+ public void testAutoGrouped_singleOngoing_appGroupOngoingChild() {
+ final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ if (i == 0) {
+ sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ }
+ notifications.add(sbn);
}
- mGroupHelper.onNotificationRemoved(notifications.get(0));
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
- verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
+ // app group the ongoing child
+ StatusBarNotification sbn = getSbn(pkg, 0, "0", UserHandle.SYSTEM, "app group now");
+ mGroupHelper.onNotificationPosted(sbn, true);
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg), AUTOGROUP_AT_COUNT);
+ // Summary is no longer ongoing
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
}
-
@Test
- public void testAutoGroupCount_UpdateToNoneOngoingNotification() {
+ public void testAutoGrouped_singleOngoing_removeNonOngoingChild() {
final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ if (i == 0) {
+ sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ }
+ notifications.add(sbn);
}
for (StatusBarNotification sbn: notifications) {
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ mGroupHelper.onNotificationPosted(sbn, false);
}
- for (StatusBarNotification sbn: notifications) {
- mGroupHelper.onNotificationPosted(sbn, true);
+ // remove ongoing
+ mGroupHelper.onNotificationRemoved(notifications.get(1));
+
+ // Summary is still ongoing
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ }
+
+ @Test
+ public void testAutoGrouped_allAutoCancel_updateChildNotAutoCancel() {
+ final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ notifications.add(sbn);
}
- notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- mGroupHelper.onNotificationUpdated(notifications.get(0));
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
- verify(mCallback, times(1))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
+ // One notification is no longer autocancelable
+ notifications.get(0).getNotification().flags &= ~FLAG_AUTO_CANCEL;
+ mGroupHelper.onNotificationPosted(notifications.get(0), true);
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg), 1);
+ // Summary should no longer be autocancelable
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
}
@Test
- public void testAutoGroupCount_AddOneOngoingNotification() {
+ public void testAutoGrouped_almostAllAutoCancel_updateChildAutoCancel() {
final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ if (i != 0) {
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ }
+ notifications.add(sbn);
}
- StatusBarNotification sbn = notifications.get(AUTOGROUP_AT_COUNT);
- sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
-
- for (StatusBarNotification current: notifications) {
- mGroupHelper.onNotificationPosted(current, true);
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
+ // Missing notification is now autocancelable
+ notifications.get(0).getNotification().flags |= FLAG_AUTO_CANCEL;
+ mGroupHelper.onNotificationPosted(notifications.get(0), true);
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg), 1);
+ // Summary should now autocancelable
+ verify(mCallback).updateAutogroupSummary(
+ anyInt(), anyString(), eq(BASE_FLAGS | FLAG_AUTO_CANCEL));
}
@Test
- public void testAutoGroupCount_UpdateNoneOngoing() {
+ public void testAutoGrouped_allAutoCancel_updateChildAppGrouped() {
final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
ArrayList<StatusBarNotification> notifications = new ArrayList<>();
- for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
- notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ notifications.add(sbn);
}
for (StatusBarNotification sbn: notifications) {
- sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
+
+ // One notification is now grouped by app
+ StatusBarNotification sbn = getSbn(pkg, 0, "0", UserHandle.SYSTEM, "app group now");
+ mGroupHelper.onNotificationPosted(sbn, true);
+
+ // Summary should be still be autocancelable
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
+ }
+
+ @Test
+ public void testAutoGrouped_allAutoCancel_removeChild() {
+ final String pkg = "package";
+
+ // Post AUTOGROUP_AT_COUNT ongoing notifications
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ sbn.getNotification().flags |= FLAG_AUTO_CANCEL;
+ notifications.add(sbn);
}
for (StatusBarNotification sbn: notifications) {
- mGroupHelper.onNotificationPosted(sbn, true);
+ mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(0))
- .updateAutogroupSummary(anyInt(), anyString(), eq(true));
+ mGroupHelper.onNotificationRemoved(notifications.get(0));
- int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg), 0);
+ // Summary should still be autocancelable
+ verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyInt());
}
-
@Test
- public void testDropToZeroRemoveGroup() throws Exception {
+ public void testDropToZeroRemoveGroup() {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -371,7 +564,8 @@ public class GroupHelperTest extends UiServiceTestCase {
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false));
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -390,7 +584,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
@Test
- public void testAppStartsGrouping() throws Exception {
+ public void testAppStartsGrouping() {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -399,7 +593,7 @@ public class GroupHelperTest extends UiServiceTestCase {
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(
- anyInt(), eq(pkg), anyString(), anyBoolean());
+ anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -408,9 +602,10 @@ public class GroupHelperTest extends UiServiceTestCase {
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
final StatusBarNotification sbn =
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
- mGroupHelper.onNotificationPosted(sbn, false);
+ sbn.setOverrideGroupKey("autogrouped");
+ mGroupHelper.onNotificationPosted(sbn, true);
verify(mCallback, times(1)).removeAutoGroup(sbn.getKey());
- if (i < AUTOGROUP_AT_COUNT -1) {
+ if (i < AUTOGROUP_AT_COUNT - 1) {
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
}
}
@@ -418,8 +613,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
@Test
- public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
- throws Exception {
+ public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -427,7 +621,8 @@ public class GroupHelperTest extends UiServiceTestCase {
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false));
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(BASE_FLAGS));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -441,10 +636,7 @@ public class GroupHelperTest extends UiServiceTestCase {
Mockito.reset(mCallback);
// only one child remains
- Map<String, LinkedHashSet<String>> ungroupedForUser =
- mGroupHelper.mUngroupedNotifications.get(UserHandle.USER_SYSTEM);
- assertNotNull(ungroupedForUser);
- assertEquals(1, ungroupedForUser.get(pkg).size());
+ assertEquals(1, mGroupHelper.getNotGroupedByAppCount(UserHandle.USER_SYSTEM, pkg));
// Add new notification; it should be autogrouped even though the total count is
// < AUTOGROUP_AT_COUNT
@@ -454,5 +646,8 @@ public class GroupHelperTest extends UiServiceTestCase {
verify(mCallback, times(1)).addAutoGroup(sbn.getKey());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), eq(BASE_FLAGS));
+ verify(mCallback, never()).addAutoGroupSummary(
+ anyInt(), anyString(), anyString(), anyInt());
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index eceb589bba76..9cfdaa7cad0c 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -754,13 +754,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
String groupKey, boolean isSummary) {
+ return generateNotificationRecord(channel, id, "tag" + System.currentTimeMillis(), groupKey,
+ isSummary);
+ }
+
+ private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
+ String tag, String groupKey, boolean isSummary) {
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setGroup(groupKey)
.setGroupSummary(isSummary);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
- "tag" + System.currentTimeMillis(), mUid, 0,
+ tag, mUid, 0,
nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
@@ -1899,7 +1905,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mSummaryByGroupKey.put("pkg", summary);
mService.mAutobundledSummaries.put(0, new ArrayMap<>());
mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
- mService.updateAutobundledSummaryFlags(0, "pkg", true, false);
+ mService.updateAutobundledSummaryFlags(
+ 0, "pkg", GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, false);
assertTrue(summary.getSbn().isOngoing());
}
@@ -1915,7 +1922,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
mService.mSummaryByGroupKey.put("pkg", summary);
- mService.updateAutobundledSummaryFlags(0, "pkg", false, false);
+ mService.updateAutobundledSummaryFlags(0, "pkg", GroupHelper.BASE_FLAGS, false);
assertFalse(summary.getSbn().isOngoing());
}
@@ -2897,7 +2904,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
NotificationRecord r = mService.createAutoGroupSummary(
- temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false);
+ temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), 0);
assertThat(r.isImportanceFixed()).isTrue();
}
@@ -4213,7 +4220,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testOnlyAutogroupIfGroupChanged_noPriorNoti_autogroups() throws Exception {
+ public void testOnlyAutogroupIfNeeded_newNotification_ghUpdate() {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
@@ -4226,17 +4233,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testOnlyAutogroupIfGroupChanged_groupChanged_autogroups()
- throws Exception {
- NotificationRecord r =
- generateNotificationRecord(mTestNotificationChannel, 0, "group", false);
+ public void testOnlyAutogroupIfNeeded_groupChanged_ghUpdate() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0,
+ "testOnlyAutogroupIfNeeded_groupChanged_ghUpdate", "group", false);
mService.addNotification(r);
- r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
- mService.addEnqueuedNotification(r);
+ NotificationRecord update = generateNotificationRecord(mTestNotificationChannel, 0,
+ "testOnlyAutogroupIfNeeded_groupChanged_ghUpdate", null, false);
+ mService.addEnqueuedNotification(update);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
- r.getUid(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(update.getKey(),
+ update.getSbn().getPackageName(), update.getUid(),
+ SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -4244,16 +4252,39 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testOnlyAutogroupIfGroupChanged_noGroupChanged_autogroups()
- throws Exception {
- NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group",
- false);
+ public void testOnlyAutogroupIfNeeded_flagsChanged_ghUpdate() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0,
+ "testOnlyAutogroupIfNeeded_flagsChanged_ghUpdate", "group", false);
mService.addNotification(r);
- mService.addEnqueuedNotification(r);
+ NotificationRecord update = generateNotificationRecord(mTestNotificationChannel, 0,
+ "testOnlyAutogroupIfNeeded_flagsChanged_ghUpdate", null, false);
+ update.getNotification().flags = FLAG_AUTO_CANCEL;
+ mService.addEnqueuedNotification(update);
NotificationManagerService.PostNotificationRunnable runnable =
- mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
- r.getUid(), SystemClock.elapsedRealtime());
+ mService.new PostNotificationRunnable(update.getKey(),
+ update.getSbn().getPackageName(), update.getUid(),
+ SystemClock.elapsedRealtime());
+ runnable.run();
+ waitForIdle();
+
+ verify(mGroupHelper, times(1)).onNotificationPosted(any(), anyBoolean());
+ }
+
+ @Test
+ public void testOnlyAutogroupIfGroupChanged_noValidChange_noGhUpdate() {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0,
+ "testOnlyAutogroupIfGroupChanged_noValidChange_noGhUpdate", null, false);
+ mService.addNotification(r);
+ NotificationRecord update = generateNotificationRecord(mTestNotificationChannel, 0,
+ "testOnlyAutogroupIfGroupChanged_noValidChange_noGhUpdate", null, false);
+ update.getNotification().color = Color.BLACK;
+ mService.addEnqueuedNotification(update);
+
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(update.getKey(),
+ update.getSbn().getPackageName(),
+ update.getUid(), SystemClock.elapsedRealtime());
runnable.run();
waitForIdle();
@@ -10220,10 +10251,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// grouphelper is a mock here, so make the calls it would make
- // add summary; wait for it to be posted
- mService.addAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(),
- true);
- waitForIdle();
+ // add summary
+ mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(),
+ nr1.getSbn().getPackageName(), nr1.getKey(),
+ GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT));
// cancel both children
mBinderService.cancelNotificationWithTag(PKG, PKG, nr0.getSbn().getTag(),
@@ -10232,9 +10263,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nr1.getSbn().getId(), nr1.getSbn().getUserId());
waitForIdle();
- // group helper would send 'remove flag' and then 'remove summary' events
- mService.updateAutobundledSummaryFlags(nr1.getUserId(), nr1.getSbn().getPackageName(),
- false, false);
+ // group helper would send 'remove summary' event
mService.clearAutogroupSummaryLocked(nr1.getUserId(), nr1.getSbn().getPackageName());
waitForIdle();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
index e6569f7e0ce2..9fe0e49c4ab8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java
@@ -98,6 +98,41 @@ public class NotificationRecordExtractorDataTest extends UiServiceTestCase {
}
@Test
+ public void testHasDiffs_autoBundled() {
+ NotificationRecord r = generateRecord();
+
+ NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData(
+ 1,
+ r.getPackageVisibilityOverride(),
+ r.canShowBadge(),
+ r.canBubble(),
+ r.getNotification().isBubbleNotification(),
+ r.getChannel(),
+ r.getGroupKey(),
+ r.getPeopleOverride(),
+ r.getSnoozeCriteria(),
+ r.getUserSentiment(),
+ r.getSuppressedVisualEffects(),
+ r.getSystemGeneratedSmartActions(),
+ r.getSmartReplies(),
+ r.getImportance(),
+ r.getRankingScore(),
+ r.isConversation(),
+ r.getProposedImportance(),
+ r.hasSensitiveContent());
+
+ Bundle signals = new Bundle();
+ signals.putString(Adjustment.KEY_GROUP_KEY, "ranker_group");
+ Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
+ r.addAdjustment(adjustment);
+ NotificationAdjustmentExtractor adjustmentExtractor = new NotificationAdjustmentExtractor();
+ adjustmentExtractor.process(r);
+
+ assertTrue(extractorData.hasDiffForRankingLocked(r, 1));
+ assertTrue(extractorData.hasDiffForLoggingLocked(r, 1));
+ }
+
+ @Test
public void testHasDiffs_sensitiveContentChange() {
NotificationRecord r = generateRecord();
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 c131c84a50ce..7092b0b5ac34 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -104,7 +104,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
when(mMockRunner.asBinder()).thenReturn(new Binder());
mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
DEFAULT_DISPLAY));
- mController.mShouldAttachNavBarToAppDuringTransition = false;
mRootHomeTask = mDefaultDisplay.getDefaultTaskDisplayArea().getRootHomeTask();
assertNotNull(mRootHomeTask);
}
@@ -814,13 +813,13 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
}
private void setupForShouldAttachNavBarDuringTransition() {
- mController.mShouldAttachNavBarToAppDuringTransition = true;
final WindowState navBar = spy(createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"));
mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
mWm.setRecentsAnimationController(mController);
doReturn(navBar).when(mController).getNavigationBarWindow();
final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy());
doReturn(displayPolicy).when(mDefaultDisplay).getDisplayPolicy();
+ doReturn(true).when(displayPolicy).shouldAttachNavBarToAppDuringTransition();
}
private static void initializeRecentsAnimationController(RecentsAnimationController controller,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 1e2fdec07d00..43b429c76749 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1410,9 +1410,9 @@ public class TransitionTests extends WindowTestsBase {
final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1);
assertNotNull(task1ChangeInfo);
assertTrue(task1ChangeInfo.hasChanged());
+ // Make sure the unrelated activity is NOT collected.
final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1);
- assertNotNull(activity1ChangeInfo);
- assertTrue(activity1ChangeInfo.hasChanged());
+ assertNull(activity1ChangeInfo);
// No need to wait for the activity in transient hide task.
assertEquals(WindowContainer.SYNC_STATE_NONE, activity1.mSyncState);
@@ -1442,6 +1442,7 @@ public class TransitionTests extends WindowTestsBase {
}
}
});
+ assertTrue(activity1.isVisible());
controller.finishTransition(closeTransition);
assertTrue(wasInFinishingTransition[0]);
assertNull(controller.mFinishingTransition);
@@ -1450,6 +1451,7 @@ public class TransitionTests extends WindowTestsBase {
assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState());
// Because task1 is occluded by task2, finishTransition should make activity1 invisible.
assertFalse(activity1.isVisibleRequested());
+ // Make sure activity1 visibility was committed
assertFalse(activity1.isVisible());
assertFalse(activity1.app.hasActivityInVisibleTask());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 6cf2b2d7a31e..74ba45c130e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -500,7 +500,6 @@ public class ZOrderingTests extends WindowTestsBase {
RecentsAnimationController controller = new RecentsAnimationController(
mWm, mockRunner, null, displayId);
spyOn(controller);
- controller.mShouldAttachNavBarToAppDuringTransition = true;
doReturn(mNavBarWindow).when(controller).getNavigationBarWindow();
mWm.setRecentsAnimationController(controller);
@@ -508,6 +507,10 @@ public class ZOrderingTests extends WindowTestsBase {
spyOn(mDisplayContent.mInputMethodWindow);
doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
+ DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ spyOn(policy);
+ doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition();
+
// create home activity
Task rootHomeTask = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 337e1f92050c..7fe8582f96de 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -27,6 +27,8 @@ import android.util.Slog;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.audio.AudioService;
+import java.util.Arrays;
+
/**
* Represents the ALSA specification, and attributes of an ALSA device.
*/
@@ -36,17 +38,21 @@ public final class UsbAlsaDevice {
private final int mCardNum;
private final int mDeviceNum;
+ private final String mAlsaCardDeviceString;
private final String mDeviceAddress;
- private final boolean mHasOutput;
- private final boolean mHasInput;
- private final boolean mIsInputHeadset;
- private final boolean mIsOutputHeadset;
- private final boolean mIsDock;
+ // The following two constant will be used as index to access arrays.
+ private static final int INPUT = 0;
+ private static final int OUTPUT = 1;
+ private static final int NUM_DIRECTIONS = 2;
+ private static final String[] DIRECTION_STR = {"INPUT", "OUTPUT"};
+ private final boolean[] mHasDevice = new boolean[NUM_DIRECTIONS];
- private boolean mSelected = false;
- private int mOutputState;
- private int mInputState;
+ private final boolean[] mIsHeadset = new boolean[NUM_DIRECTIONS];
+ private final boolean mIsDock;
+ private final int[] mDeviceType = new int[NUM_DIRECTIONS];
+ private boolean[] mIsSelected = new boolean[NUM_DIRECTIONS];
+ private int[] mState = new int[NUM_DIRECTIONS];
private UsbAlsaJackDetector mJackDetector;
private IAudioService mAudioService;
@@ -60,11 +66,13 @@ public final class UsbAlsaDevice {
mCardNum = card;
mDeviceNum = device;
mDeviceAddress = deviceAddress;
- mHasOutput = hasOutput;
- mHasInput = hasInput;
- mIsInputHeadset = isInputHeadset;
- mIsOutputHeadset = isOutputHeadset;
+ mHasDevice[OUTPUT] = hasOutput;
+ mHasDevice[INPUT] = hasInput;
+ mIsHeadset[INPUT] = isInputHeadset;
+ mIsHeadset[OUTPUT] = isOutputHeadset;
mIsDock = isDock;
+ initDeviceType();
+ mAlsaCardDeviceString = getAlsaCardDeviceString();
}
/**
@@ -104,28 +112,28 @@ public final class UsbAlsaDevice {
* @return true if the device supports output.
*/
public boolean hasOutput() {
- return mHasOutput;
+ return mHasDevice[OUTPUT];
}
/**
* @return true if the device supports input (recording).
*/
public boolean hasInput() {
- return mHasInput;
+ return mHasDevice[INPUT];
}
/**
- * @return true if the device is a headset for purposes of input.
+ * @return true if the device is a headset for purposes of output.
*/
- public boolean isInputHeadset() {
- return mIsInputHeadset;
+ public boolean isOutputHeadset() {
+ return mIsHeadset[OUTPUT];
}
/**
- * @return true if the device is a headset for purposes of output.
+ * @return true if the device is a headset for purposes of input.
*/
- public boolean isOutputHeadset() {
- return mIsOutputHeadset;
+ public boolean isInputHeadset() {
+ return mIsHeadset[INPUT];
}
/**
@@ -157,6 +165,9 @@ public final class UsbAlsaDevice {
/** Begins a jack-detection thread. */
private synchronized void startJackDetect() {
+ if (mJackDetector != null) {
+ return;
+ }
// If no jack detect capabilities exist, mJackDetector will be null.
mJackDetector = UsbAlsaJackDetector.startJackDetect(this);
}
@@ -171,75 +182,152 @@ public final class UsbAlsaDevice {
/** Start using this device as the selected USB Audio Device. */
public synchronized void start() {
- mSelected = true;
- mInputState = 0;
- mOutputState = 0;
+ startInput();
+ startOutput();
+ }
+
+ /** Start using this device as the selected USB input device. */
+ public synchronized void startInput() {
+ startDevice(INPUT);
+ }
+
+ /** Start using this device as selected USB output device. */
+ public synchronized void startOutput() {
+ startDevice(OUTPUT);
+ }
+
+ private void startDevice(int direction) {
+ if (mIsSelected[direction]) {
+ return;
+ }
+ mIsSelected[direction] = true;
+ mState[direction] = 0;
startJackDetect();
- updateWiredDeviceConnectionState(true);
+ updateWiredDeviceConnectionState(direction, true /*enable*/);
}
/** Stop using this device as the selected USB Audio Device. */
public synchronized void stop() {
- stopJackDetect();
- updateWiredDeviceConnectionState(false);
- mSelected = false;
+ stopInput();
+ stopOutput();
}
- /** Updates AudioService with the connection state of the alsaDevice.
- * Checks ALSA Jack state for inputs and outputs before reporting.
- */
- public synchronized void updateWiredDeviceConnectionState(boolean enable) {
- if (!mSelected) {
- Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!");
+ /** Stop using this device as the selected USB input device. */
+ public synchronized void stopInput() {
+ if (!mIsSelected[INPUT]) {
return;
}
- String alsaCardDeviceString = getAlsaCardDeviceString();
- if (alsaCardDeviceString == null) {
+ if (!mIsSelected[OUTPUT]) {
+ // Stop jack detection when both input and output are stopped
+ stopJackDetect();
+ }
+ updateInputWiredDeviceConnectionState(false /*enable*/);
+ mIsSelected[INPUT] = false;
+ }
+
+ /** Stop using this device as the selected USB output device. */
+ public synchronized void stopOutput() {
+ if (!mIsSelected[OUTPUT]) {
return;
}
- try {
- // Output Device
- if (mHasOutput) {
- int device = mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET
- : (mIsOutputHeadset
- ? AudioSystem.DEVICE_OUT_USB_HEADSET
- : AudioSystem.DEVICE_OUT_USB_DEVICE);
- if (DEBUG) {
- Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
- + " addr:" + alsaCardDeviceString
- + " name:" + mDeviceName);
- }
- boolean connected = isOutputJackConnected();
- Slog.i(TAG, "OUTPUT JACK connected: " + connected);
- int outputState = (enable && connected) ? 1 : 0;
- if (outputState != mOutputState) {
- mOutputState = outputState;
- AudioDeviceAttributes attributes = new AudioDeviceAttributes(device,
- alsaCardDeviceString, mDeviceName);
- mAudioService.setWiredDeviceConnectionState(attributes, outputState, TAG);
- }
- }
+ if (!mIsSelected[INPUT]) {
+ // Stop jack detection when both input and output are stopped
+ stopJackDetect();
+ }
+ updateOutputWiredDeviceConnectionState(false /*enable*/);
+ mIsSelected[OUTPUT] = false;
+ }
+
+ private void initDeviceType() {
+ mDeviceType[INPUT] = mHasDevice[INPUT]
+ ? (mIsHeadset[INPUT] ? AudioSystem.DEVICE_IN_USB_HEADSET
+ : AudioSystem.DEVICE_IN_USB_DEVICE)
+ : AudioSystem.DEVICE_NONE;
+ mDeviceType[OUTPUT] = mHasDevice[OUTPUT]
+ ? (mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET
+ : (mIsHeadset[OUTPUT] ? AudioSystem.DEVICE_OUT_USB_HEADSET
+ : AudioSystem.DEVICE_OUT_USB_DEVICE))
+ : AudioSystem.DEVICE_NONE;
+ }
- // Input Device
- if (mHasInput) {
- int device = mIsInputHeadset
- ? AudioSystem.DEVICE_IN_USB_HEADSET
- : AudioSystem.DEVICE_IN_USB_DEVICE;
- boolean connected = isInputJackConnected();
- Slog.i(TAG, "INPUT JACK connected: " + connected);
- int inputState = (enable && connected) ? 1 : 0;
- if (inputState != mInputState) {
- mInputState = inputState;
- AudioDeviceAttributes attributes = new AudioDeviceAttributes(device,
- alsaCardDeviceString, mDeviceName);
- mAudioService.setWiredDeviceConnectionState(attributes, inputState, TAG);
- }
+ /**
+ * @return the output device type that will be used to notify AudioService about device
+ * connection. If there is no output on this device, {@link AudioSystem#DEVICE_NONE}
+ * will be returned.
+ */
+ public int getOutputDeviceType() {
+ return mDeviceType[OUTPUT];
+ }
+
+ /**
+ * @return the input device type that will be used to notify AudioService about device
+ * connection. If there is no input on this device, {@link AudioSystem#DEVICE_NONE}
+ * will be returned.
+ */
+ public int getInputDeviceType() {
+ return mDeviceType[INPUT];
+ }
+
+ private boolean updateWiredDeviceConnectionState(int direction, boolean enable) {
+ if (!mIsSelected[direction]) {
+ Slog.e(TAG, "Updating wired device connection state on unselected device");
+ return false;
+ }
+ if (mDeviceType[direction] == AudioSystem.DEVICE_NONE) {
+ Slog.d(TAG,
+ "Unable to set device connection state as " + DIRECTION_STR[direction]
+ + " device type is none");
+ return false;
+ }
+ if (mAlsaCardDeviceString == null) {
+ Slog.w(TAG, "Failed to update " + DIRECTION_STR[direction] + " device connection "
+ + "state failed as alsa card device string is null");
+ return false;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(mDeviceType[direction])
+ + " addr:" + mAlsaCardDeviceString
+ + " name:" + mDeviceName);
+ }
+ boolean connected = direction == INPUT ? isInputJackConnected() : isOutputJackConnected();
+ Slog.i(TAG, DIRECTION_STR[direction] + " JACK connected: " + connected);
+ int state = (enable && connected) ? 1 : 0;
+ if (state != mState[direction]) {
+ mState[direction] = state;
+ AudioDeviceAttributes attributes = new AudioDeviceAttributes(
+ mDeviceType[direction], mAlsaCardDeviceString, mDeviceName);
+ try {
+ mAudioService.setWiredDeviceConnectionState(attributes, state, TAG);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState for "
+ + DIRECTION_STR[direction]);
+ return false;
}
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState");
}
+ return true;
}
+ /**
+ * Notify AudioService about the input device connection state.
+ *
+ * @param enable true to notify the device as connected.
+ * @return true only when it successfully notifies AudioService about the device
+ * connection state.
+ */
+ public synchronized boolean updateInputWiredDeviceConnectionState(boolean enable) {
+ return updateWiredDeviceConnectionState(INPUT, enable);
+ }
+
+ /**
+ * Notify AudioService about the output device connection state.
+ *
+ * @param enable true to notify the device as connected.
+ * @return true only when it successfully notifies AudioService about the device
+ * connection state.
+ */
+ public synchronized boolean updateOutputWiredDeviceConnectionState(boolean enable) {
+ return updateWiredDeviceConnectionState(OUTPUT, enable);
+ }
/**
* @Override
@@ -249,8 +337,8 @@ public final class UsbAlsaDevice {
return "UsbAlsaDevice: [card: " + mCardNum
+ ", device: " + mDeviceNum
+ ", name: " + mDeviceName
- + ", hasOutput: " + mHasOutput
- + ", hasInput: " + mHasInput + "]";
+ + ", hasOutput: " + mHasDevice[OUTPUT]
+ + ", hasInput: " + mHasDevice[INPUT] + "]";
}
/**
@@ -262,8 +350,8 @@ public final class UsbAlsaDevice {
dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum);
dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum);
dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName);
- dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput);
- dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput);
+ dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasDevice[OUTPUT]);
+ dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasDevice[INPUT]);
dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress);
dump.end(token);
@@ -294,10 +382,8 @@ public final class UsbAlsaDevice {
UsbAlsaDevice other = (UsbAlsaDevice) obj;
return (mCardNum == other.mCardNum
&& mDeviceNum == other.mDeviceNum
- && mHasOutput == other.mHasOutput
- && mHasInput == other.mHasInput
- && mIsInputHeadset == other.mIsInputHeadset
- && mIsOutputHeadset == other.mIsOutputHeadset
+ && Arrays.equals(mHasDevice, other.mHasDevice)
+ && Arrays.equals(mIsHeadset, other.mIsHeadset)
&& mIsDock == other.mIsDock);
}
@@ -310,10 +396,10 @@ public final class UsbAlsaDevice {
int result = 1;
result = prime * result + mCardNum;
result = prime * result + mDeviceNum;
- result = prime * result + (mHasOutput ? 0 : 1);
- result = prime * result + (mHasInput ? 0 : 1);
- result = prime * result + (mIsInputHeadset ? 0 : 1);
- result = prime * result + (mIsOutputHeadset ? 0 : 1);
+ result = prime * result + (mHasDevice[OUTPUT] ? 0 : 1);
+ result = prime * result + (mHasDevice[INPUT] ? 0 : 1);
+ result = prime * result + (mIsHeadset[INPUT] ? 0 : 1);
+ result = prime * result + (mIsHeadset[OUTPUT] ? 0 : 1);
result = prime * result + (mIsDock ? 0 : 1);
return result;
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
index c4988478df71..d4f0b59dd7f2 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java
@@ -81,7 +81,8 @@ public final class UsbAlsaJackDetector implements Runnable {
if (mStopJackDetect) {
return false;
}
- mAlsaDevice.updateWiredDeviceConnectionState(true);
+ mAlsaDevice.updateOutputWiredDeviceConnectionState(true);
+ mAlsaDevice.updateInputWiredDeviceConnectionState(true);
}
return true;
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index aa1d556d02d3..99881e194b07 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -20,12 +20,14 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbDevice;
+import android.media.AudioManager;
import android.media.IAudioService;
import android.media.midi.MidiDeviceInfo;
import android.os.Bundle;
import android.os.FileObserver;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.service.usb.UsbAlsaManagerProto;
import android.util.Slog;
@@ -42,6 +44,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Stack;
/**
* UsbAlsaManager manages USB audio and MIDI devices.
@@ -51,8 +54,9 @@ public final class UsbAlsaManager {
private static final boolean DEBUG = false;
// Flag to turn on/off multi-peripheral select mode
- // Set to true to have single-device-only mode
- private static final boolean mIsSingleMode = true;
+ // Set to true to have multi-devices mode
+ private static final boolean IS_MULTI_MODE = SystemProperties.getBoolean(
+ "ro.audio.multi_usb_mode", false /*def*/);
private static final String ALSA_DIRECTORY = "/dev/snd/";
@@ -70,7 +74,11 @@ public final class UsbAlsaManager {
// this is needed to map USB devices to ALSA Audio Devices, especially to remove an
// ALSA device when we are notified that its associated USB device has been removed.
private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>();
- private UsbAlsaDevice mSelectedDevice;
+ // A map from device type to attached devices. Given the audio framework only supports
+ // single device connection per device type, only the last attached device will be
+ // connected to audio framework. Once the last device is removed, previous device can
+ // be connected to audio framework.
+ private HashMap<Integer, Stack<UsbAlsaDevice>> mAttachedDevices = new HashMap<>();
//
// Device Denylist
@@ -162,11 +170,6 @@ public final class UsbAlsaManager {
Slog.d(TAG, "selectAlsaDevice() " + alsaDevice);
}
- // This must be where an existing USB audio device is deselected.... (I think)
- if (mIsSingleMode && mSelectedDevice != null) {
- deselectAlsaDevice();
- }
-
// FIXME Does not yet handle the case where the setting is changed
// after device connection. Ideally we should handle the settings change
// in SettingsObserver. Here we should log that a USB device is connected
@@ -178,21 +181,18 @@ public final class UsbAlsaManager {
return;
}
- mSelectedDevice = alsaDevice;
alsaDevice.start();
+
if (DEBUG) {
Slog.d(TAG, "selectAlsaDevice() - done.");
}
}
- private synchronized void deselectAlsaDevice() {
+ private synchronized void deselectAlsaDevice(UsbAlsaDevice selectedDevice) {
if (DEBUG) {
- Slog.d(TAG, "deselectAlsaDevice() mSelectedDevice " + mSelectedDevice);
- }
- if (mSelectedDevice != null) {
- mSelectedDevice.stop();
- mSelectedDevice = null;
+ Slog.d(TAG, "deselectAlsaDevice() selectedDevice " + selectedDevice);
}
+ selectedDevice.stop();
}
private int getAlsaDeviceListIndexFor(String deviceAddress) {
@@ -204,32 +204,86 @@ public final class UsbAlsaManager {
return -1;
}
- private UsbAlsaDevice removeAlsaDeviceFromList(String deviceAddress) {
+ private void addDeviceToAttachedDevicesMap(int deviceType, UsbAlsaDevice device) {
+ if (deviceType == AudioManager.DEVICE_NONE) {
+ Slog.i(TAG, "Ignore caching device as the type is NONE, device=" + device);
+ return;
+ }
+ Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType);
+ if (devices == null) {
+ mAttachedDevices.put(deviceType, new Stack<>());
+ devices = mAttachedDevices.get(deviceType);
+ }
+ devices.push(device);
+ }
+
+ private void addAlsaDevice(UsbAlsaDevice device) {
+ mAlsaDevices.add(0, device);
+ addDeviceToAttachedDevicesMap(device.getInputDeviceType(), device);
+ addDeviceToAttachedDevicesMap(device.getOutputDeviceType(), device);
+ }
+
+ private void removeDeviceFromAttachedDevicesMap(int deviceType, UsbAlsaDevice device) {
+ Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType);
+ if (devices == null) {
+ return;
+ }
+ devices.remove(device);
+ if (devices.isEmpty()) {
+ mAttachedDevices.remove(deviceType);
+ }
+ }
+
+ private UsbAlsaDevice removeAlsaDevice(String deviceAddress) {
int index = getAlsaDeviceListIndexFor(deviceAddress);
if (index > -1) {
- return mAlsaDevices.remove(index);
+ UsbAlsaDevice device = mAlsaDevices.remove(index);
+ removeDeviceFromAttachedDevicesMap(device.getOutputDeviceType(), device);
+ removeDeviceFromAttachedDevicesMap(device.getInputDeviceType(), device);
+ return device;
} else {
return null;
}
}
- /* package */ UsbAlsaDevice selectDefaultDevice() {
+ private UsbAlsaDevice selectDefaultDevice(int deviceType) {
if (DEBUG) {
- Slog.d(TAG, "selectDefaultDevice()");
+ Slog.d(TAG, "selectDefaultDevice():" + deviceType);
}
- if (mAlsaDevices.size() > 0) {
- UsbAlsaDevice alsaDevice = mAlsaDevices.get(0);
- if (DEBUG) {
- Slog.d(TAG, " alsaDevice:" + alsaDevice);
- }
- if (alsaDevice != null) {
- selectAlsaDevice(alsaDevice);
- }
- return alsaDevice;
- } else {
+ Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType);
+ if (devices == null || devices.isEmpty()) {
return null;
}
+ UsbAlsaDevice alsaDevice = devices.peek();
+ Slog.d(TAG, "select default device:" + alsaDevice);
+ if (AudioManager.isInputDevice(deviceType)) {
+ alsaDevice.startInput();
+ } else {
+ alsaDevice.startOutput();
+ }
+ return alsaDevice;
+ }
+
+ private void deselectCurrentDevice(int deviceType) {
+ if (DEBUG) {
+ Slog.d(TAG, "deselectCurrentDevice():" + deviceType);
+ }
+ if (deviceType == AudioManager.DEVICE_NONE) {
+ return;
+ }
+
+ Stack<UsbAlsaDevice> devices = mAttachedDevices.get(deviceType);
+ if (devices == null || devices.isEmpty()) {
+ return;
+ }
+ UsbAlsaDevice alsaDevice = devices.peek();
+ Slog.d(TAG, "deselect current device:" + alsaDevice);
+ if (AudioManager.isInputDevice(deviceType)) {
+ alsaDevice.stopInput();
+ } else {
+ alsaDevice.stopOutput();
+ }
}
/* package */ void usbDeviceAdded(String deviceAddress, UsbDevice usbDevice,
@@ -246,6 +300,7 @@ public final class UsbAlsaManager {
AlsaCardsParser.AlsaCardRecord cardRec =
mCardsParser.findCardNumFor(deviceAddress);
if (cardRec == null) {
+ Slog.e(TAG, "usbDeviceAdded(): cannot find sound card for " + deviceAddress);
return;
}
@@ -275,12 +330,19 @@ public final class UsbAlsaManager {
new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
deviceAddress, hasOutput, hasInput,
isInputHeadset, isOutputHeadset, isDock);
- if (alsaDevice != null) {
- alsaDevice.setDeviceNameAndDescription(
- cardRec.getCardName(), cardRec.getCardDescription());
- mAlsaDevices.add(0, alsaDevice);
- selectAlsaDevice(alsaDevice);
+ alsaDevice.setDeviceNameAndDescription(
+ cardRec.getCardName(), cardRec.getCardDescription());
+ if (IS_MULTI_MODE) {
+ deselectCurrentDevice(alsaDevice.getInputDeviceType());
+ deselectCurrentDevice(alsaDevice.getOutputDeviceType());
+ } else {
+ // At single mode, the first device is the selected device.
+ if (!mAlsaDevices.isEmpty()) {
+ deselectAlsaDevice(mAlsaDevices.get(0));
+ }
}
+ addAlsaDevice(alsaDevice);
+ selectAlsaDevice(alsaDevice);
}
addMidiDevice(deviceAddress, usbDevice, parser, cardRec);
@@ -346,12 +408,20 @@ public final class UsbAlsaManager {
}
// Audio
- UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress);
+ UsbAlsaDevice alsaDevice = removeAlsaDevice(deviceAddress);
Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice);
- if (alsaDevice != null && alsaDevice == mSelectedDevice) {
+ if (alsaDevice != null) {
waitForAlsaDevice(alsaDevice.getCardNum(), false /*isAdded*/);
- deselectAlsaDevice();
- selectDefaultDevice(); // if there any external devices left, select one of them
+ deselectAlsaDevice(alsaDevice);
+ if (IS_MULTI_MODE) {
+ selectDefaultDevice(alsaDevice.getOutputDeviceType());
+ selectDefaultDevice(alsaDevice.getInputDeviceType());
+ } else {
+ // If there are any external devices left, select the latest attached one
+ if (!mAlsaDevices.isEmpty() && mAlsaDevices.get(0) != null) {
+ selectAlsaDevice(mAlsaDevices.get(0));
+ }
+ }
}
// MIDI
@@ -362,7 +432,6 @@ public final class UsbAlsaManager {
}
logDevices("usbDeviceRemoved()");
-
}
/* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
diff --git a/services/voiceinteraction/TEST_MAPPING b/services/voiceinteraction/TEST_MAPPING
index 5fe1c8d2ecb0..f098155a9bf7 100644
--- a/services/voiceinteraction/TEST_MAPPING
+++ b/services/voiceinteraction/TEST_MAPPING
@@ -5,6 +5,9 @@
"options": [
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceStressTest"
}
]
},
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 5efd158133ed..07dc1c66bc4d 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -315,12 +315,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig,
int keyphraseId, boolean runInBatterySaverMode) {
synchronized (mLock) {
+ // TODO Remove previous callback handling
IRecognitionStatusCallback oldCallback = modelData.getCallback();
if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
Slog.w(TAG, "Canceling previous recognition for model id: "
+ modelData.getModelId());
try {
- oldCallback.onError(STATUS_ERROR);
+ oldCallback.onPreempted();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped", e);
}
@@ -759,15 +760,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
onRecognitionAbortLocked(event);
break;
case SoundTrigger.RECOGNITION_STATUS_FAILURE:
- // Fire failures to all listeners since it's not tied to a keyphrase.
- onRecognitionFailureLocked();
- break;
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
case SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE:
if (isKeyphraseRecognitionEvent(event)) {
- onKeyphraseRecognitionSuccessLocked((KeyphraseRecognitionEvent) event);
+ onKeyphraseRecognitionLocked((KeyphraseRecognitionEvent) event);
} else {
- onGenericRecognitionSuccessLocked((GenericRecognitionEvent) event);
+ onGenericRecognitionLocked((GenericRecognitionEvent) event);
}
break;
}
@@ -778,7 +776,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return event instanceof KeyphraseRecognitionEvent;
}
- private void onGenericRecognitionSuccessLocked(GenericRecognitionEvent event) {
+ private void onGenericRecognitionLocked(GenericRecognitionEvent event) {
MetricsLogger.count(mContext, "sth_generic_recognition_event", 1);
if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS
&& event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
@@ -901,17 +899,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
}
- private void onRecognitionFailureLocked() {
- Slog.w(TAG, "Recognition failure");
- MetricsLogger.count(mContext, "sth_recognition_failure_event", 1);
- try {
- sendErrorCallbacksToAllLocked(STATUS_ERROR);
- } finally {
- internalClearModelStateLocked();
- internalClearGlobalStateLocked();
- }
- }
-
private int getKeyphraseIdFromEvent(KeyphraseRecognitionEvent event) {
if (event == null) {
Slog.w(TAG, "Null RecognitionEvent received.");
@@ -927,7 +914,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return keyphraseExtras[0].id;
}
- private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) {
+ private void onKeyphraseRecognitionLocked(KeyphraseRecognitionEvent event) {
Slog.i(TAG, "Recognition success");
MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1);
int keyphraseId = getKeyphraseIdFromEvent(event);
@@ -1001,7 +988,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private void onServiceDiedLocked() {
try {
MetricsLogger.count(mContext, "sth_service_died", 1);
- sendErrorCallbacksToAllLocked(SoundTrigger.STATUS_DEAD_OBJECT);
+ for (ModelData modelData : mModelDataMap.values()) {
+ IRecognitionStatusCallback callback = modelData.getCallback();
+ if (callback != null) {
+ try {
+ callback.onModuleDied();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException send moduleDied for model handle " +
+ modelData.getHandle(), e);
+ }
+ }
+ }
} finally {
internalClearModelStateLocked();
internalClearGlobalStateLocked();
@@ -1111,21 +1108,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
}
- // Sends an error callback to all models with a valid registered callback.
- private void sendErrorCallbacksToAllLocked(int errorCode) {
- for (ModelData modelData : mModelDataMap.values()) {
- IRecognitionStatusCallback callback = modelData.getCallback();
- if (callback != null) {
- try {
- callback.onError(errorCode);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException sendErrorCallbacksToAllLocked for model handle " +
- modelData.getHandle(), e);
- }
- }
- }
- }
-
/**
* Stops and unloads all models. This is intended as a clean-up call with the expectation that
* this instance is not used after.
@@ -1342,11 +1324,11 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
// Notify of error if needed.
if (notifyClientOnError) {
try {
- callback.onError(status);
+ callback.onResumeFailed(status);
} catch (DeadObjectException e) {
forceStopAndUnloadModelLocked(modelData, e);
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in onError", e);
+ Slog.w(TAG, "RemoteException in onResumeFailed", e);
}
}
} else {
@@ -1382,15 +1364,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
status = mModule.stopRecognition(modelData.getHandle());
if (status != SoundTrigger.STATUS_OK) {
- Slog.w(TAG, "stopRecognition call failed with " + status);
+ Slog.e(TAG, "stopRecognition call failed with " + status);
MetricsLogger.count(mContext, "sth_stop_recognition_error", 1);
if (notify) {
try {
- callback.onError(status);
+ callback.onPauseFailed(status);
} catch (DeadObjectException e) {
forceStopAndUnloadModelLocked(modelData, e);
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in onError", e);
+ Slog.w(TAG, "RemoteException in onPauseFailed", e);
}
}
} else {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 203a3e74d9da..1bbea89f5acb 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -25,6 +25,7 @@ import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.hardware.soundtrigger.SoundTrigger.STATUS_BAD_VALUE;
+import static android.hardware.soundtrigger.SoundTrigger.STATUS_DEAD_OBJECT;
import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY;
@@ -1388,8 +1389,7 @@ public class SoundTriggerService extends SystemService {
}));
}
- @Override
- public void onError(int status) {
+ private void onError(int status) {
if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
sEventLogger.enqueue(new EventLogger.StringEvent(mPuuid
@@ -1412,6 +1412,30 @@ public class SoundTriggerService extends SystemService {
}
@Override
+ public void onPreempted() {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onPreempted");
+ onError(STATUS_ERROR);
+ }
+
+ @Override
+ public void onModuleDied() {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onModuleDied");
+ onError(STATUS_DEAD_OBJECT);
+ }
+
+ @Override
+ public void onResumeFailed(int status) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onResumeFailed: " + status);
+ onError(status);
+ }
+
+ @Override
+ public void onPauseFailed(int status) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onPauseFailed: " + status);
+ onError(status);
+ }
+
+ @Override
public void onRecognitionPaused() {
Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index f3cb9baedd4b..a1adee732701 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -56,6 +56,7 @@ import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.ISandboxedDetectionService;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
+import android.service.voice.SoundTriggerFailure;
import android.service.voice.VisualQueryDetectionService;
import android.service.voice.VisualQueryDetectionServiceFailure;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
@@ -576,8 +577,31 @@ final class HotwordDetectionConnection {
}
@Override
- public void onError(int status) throws RemoteException {
- mExternalCallback.onError(status);
+ public void onPreempted() throws RemoteException {
+ mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure(
+ SoundTriggerFailure.ERROR_CODE_UNEXPECTED_PREEMPTION,
+ "Unexpected startRecognition on already started ST session"));
+ }
+
+ @Override
+ public void onModuleDied() throws RemoteException {
+ mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure(
+ SoundTriggerFailure.ERROR_CODE_MODULE_DIED,
+ "STHAL died"));
+ }
+
+ @Override
+ public void onResumeFailed(int status) throws RemoteException {
+ mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure(
+ SoundTriggerFailure.ERROR_CODE_RECOGNITION_RESUME_FAILED,
+ "STService recognition resume failed with: " + status));
+ }
+
+ @Override
+ public void onPauseFailed(int status) throws RemoteException {
+ mExternalCallback.onSoundTriggerFailure(new SoundTriggerFailure(
+ SoundTriggerFailure.ERROR_CODE_RECOGNITION_RESUME_FAILED,
+ "STService recognition pause failed with: " + status));
}
@Override
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 78c61964edfd..559faf9b20de 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3142,10 +3142,10 @@ public class SubscriptionManager {
*
* Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
* true). To check for permissions for non-embedded subscription as well,
+ * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
*
* @param info The subscription to check.
* @return whether the app is authorized to manage this subscription per its metadata.
- *
* @see android.telephony.TelephonyManager#hasCarrierPrivileges
*/
public boolean canManageSubscription(SubscriptionInfo info) {
@@ -3159,12 +3159,12 @@ public class SubscriptionManager {
*
* Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
* true). To check for permissions for non-embedded subscription as well,
+ * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
*
* @param info The subscription to check.
* @param packageName Package name of the app to check.
*
* @return whether the app is authorized to manage this subscription per its access rules.
- *
* @see android.telephony.TelephonyManager#hasCarrierPrivileges
* @hide
*/
diff --git a/telephony/java/android/telephony/satellite/AntennaDirection.aidl b/telephony/java/android/telephony/satellite/AntennaDirection.aidl
new file mode 100644
index 000000000000..c838f6fbb8ac
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/AntennaDirection.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable AntennaDirection;
diff --git a/telephony/java/android/telephony/satellite/AntennaDirection.java b/telephony/java/android/telephony/satellite/AntennaDirection.java
new file mode 100644
index 000000000000..02b0bc7364a2
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/AntennaDirection.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Antenna direction is provided as X/Y/Z values corresponding to the direction of the antenna
+ * main lobe as a unit vector in CTIA coordinate system (as specified in Appendix A of Wireless
+ * device CTIA OTAn test plan). CTIA coordinate system is defined relative to device’s screen
+ * when the device is held in default portrait mode with screen facing the user:
+ *
+ * Z axis is vertical along the plane of the device with positive Z pointing up and negative z
+ * pointing towards bottom of the device
+ * Y axis is horizontal along the plane of the device with positive Y pointing towards right of
+ * the phone screen and negative Y pointing towards left
+ * X axis is orthogonal to the Y-Z plane (phone screen), pointing away from the phone screen for
+ * positive X and pointing away from back of the phone for negative X.
+ * @hide
+ */
+public final class AntennaDirection implements Parcelable {
+ /** Antenna x axis direction. */
+ private float mX;
+
+ /** Antenna y axis direction. */
+ private float mY;
+
+ /** Antenna z axis direction. */
+ private float mZ;
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public AntennaDirection(float x, float y, float z) {
+ mX = x;
+ mY = y;
+ mZ = z;
+ }
+
+ private AntennaDirection(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeFloat(mX);
+ out.writeFloat(mY);
+ out.writeFloat(mZ);
+ }
+
+ @NonNull
+ public static final Creator<AntennaDirection> CREATOR =
+ new Creator<>() {
+ @Override
+ public AntennaDirection createFromParcel(Parcel in) {
+ return new AntennaDirection(in);
+ }
+
+ @Override
+ public AntennaDirection[] newArray(int size) {
+ return new AntennaDirection[size];
+ }
+ };
+
+ @Override
+ @NonNull public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("X:");
+ sb.append(mX);
+ sb.append(",");
+
+ sb.append("Y:");
+ sb.append(mY);
+ sb.append(",");
+
+ sb.append("Z:");
+ sb.append(mZ);
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AntennaDirection that = (AntennaDirection) o;
+ return mX == that.mX
+ && mY == that.mY
+ && mZ == that.mZ;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mX, mY, mZ);
+ }
+
+ public float getX() {
+ return mX;
+ }
+
+ public float getY() {
+ return mY;
+ }
+
+ public float getZ() {
+ return mZ;
+ }
+
+ private void readFromParcel(Parcel in) {
+ mX = in.readFloat();
+ mY = in.readFloat();
+ mZ = in.readFloat();
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.aidl b/telephony/java/android/telephony/satellite/AntennaPosition.aidl
new file mode 100644
index 000000000000..00525624329c
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/AntennaPosition.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable AntennaPosition;
diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java
new file mode 100644
index 000000000000..eefc8b00f8e8
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/AntennaPosition.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Antenna Position received from satellite modem which gives information about antenna
+ * direction to be used with satellite communication and suggested device hold positions.
+ * @hide
+ */
+public final class AntennaPosition implements Parcelable {
+ /** Antenna direction used for satellite communication. */
+ @NonNull AntennaDirection mAntennaDirection;
+
+ /** Enum corresponding to device hold position to be used by the end user. */
+ @SatelliteManager.DeviceHoldPosition int mSuggestedHoldPosition;
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public AntennaPosition(@NonNull AntennaDirection antennaDirection, int suggestedHoldPosition) {
+ mAntennaDirection = antennaDirection;
+ mSuggestedHoldPosition = suggestedHoldPosition;
+ }
+
+ private AntennaPosition(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(mAntennaDirection, flags);
+ out.writeInt(mSuggestedHoldPosition);
+ }
+
+ @NonNull
+ public static final Creator<AntennaPosition> CREATOR =
+ new Creator<>() {
+ @Override
+ public AntennaPosition createFromParcel(Parcel in) {
+ return new AntennaPosition(in);
+ }
+
+ @Override
+ public AntennaPosition[] newArray(int size) {
+ return new AntennaPosition[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AntennaPosition that = (AntennaPosition) o;
+ return Objects.equals(mAntennaDirection, that.mAntennaDirection)
+ && mSuggestedHoldPosition == that.mSuggestedHoldPosition;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAntennaDirection, mSuggestedHoldPosition);
+ }
+
+ @Override
+ @NonNull public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("antennaDirection:");
+ sb.append(mAntennaDirection);
+ sb.append(",");
+
+ sb.append("suggestedHoldPosition:");
+ sb.append(mSuggestedHoldPosition);
+ return sb.toString();
+ }
+
+ @NonNull
+ public AntennaDirection getAntennaDirection() {
+ return mAntennaDirection;
+ }
+
+ @SatelliteManager.DeviceHoldPosition
+ public int getSuggestedHoldPosition() {
+ return mSuggestedHoldPosition;
+ }
+
+ private void readFromParcel(Parcel in) {
+ mAntennaDirection = in.readParcelable(AntennaDirection.class.getClassLoader(),
+ AntennaDirection.class);
+ mSuggestedHoldPosition = in.readInt();
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
index 87c8db317195..00928904a4c4 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
@@ -21,7 +21,10 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -44,15 +47,25 @@ public final class SatelliteCapabilities implements Parcelable {
private int mMaxBytesPerOutgoingDatagram;
/**
+ * Antenna Position received from satellite modem which gives information about antenna
+ * direction to be used with satellite communication and suggested device hold positions.
+ * Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition
+ */
+ @NonNull
+ private Map<Integer, AntennaPosition> mAntennaPositionMap;
+
+ /**
* @hide
*/
@UnsupportedAppUsage
public SatelliteCapabilities(Set<Integer> supportedRadioTechnologies,
- boolean isPointingRequired, int maxBytesPerOutgoingDatagram) {
+ boolean isPointingRequired, int maxBytesPerOutgoingDatagram,
+ @NonNull Map<Integer, AntennaPosition> antennaPositionMap) {
mSupportedRadioTechnologies = supportedRadioTechnologies == null
? new HashSet<>() : supportedRadioTechnologies;
mIsPointingRequired = isPointingRequired;
mMaxBytesPerOutgoingDatagram = maxBytesPerOutgoingDatagram;
+ mAntennaPositionMap = antennaPositionMap;
}
private SatelliteCapabilities(Parcel in) {
@@ -77,6 +90,17 @@ public final class SatelliteCapabilities implements Parcelable {
out.writeBoolean(mIsPointingRequired);
out.writeInt(mMaxBytesPerOutgoingDatagram);
+
+ if (mAntennaPositionMap != null && !mAntennaPositionMap.isEmpty()) {
+ int size = mAntennaPositionMap.size();
+ out.writeInt(size);
+ for (Map.Entry<Integer, AntennaPosition> entry : mAntennaPositionMap.entrySet()) {
+ out.writeInt(entry.getKey());
+ out.writeParcelable(entry.getValue(), flags);
+ }
+ } else {
+ out.writeInt(0);
+ }
}
@NonNull public static final Creator<SatelliteCapabilities> CREATOR = new Creator<>() {
@@ -109,11 +133,32 @@ public final class SatelliteCapabilities implements Parcelable {
sb.append(mIsPointingRequired);
sb.append(",");
- sb.append("maxBytesPerOutgoingDatagram");
+ sb.append("maxBytesPerOutgoingDatagram:");
sb.append(mMaxBytesPerOutgoingDatagram);
+ sb.append(",");
+
+ sb.append("antennaPositionMap:");
+ sb.append(mAntennaPositionMap);
return sb.toString();
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SatelliteCapabilities that = (SatelliteCapabilities) o;
+ return Objects.equals(mSupportedRadioTechnologies, that.mSupportedRadioTechnologies)
+ && mIsPointingRequired == that.mIsPointingRequired
+ && mMaxBytesPerOutgoingDatagram == that.mMaxBytesPerOutgoingDatagram
+ && Objects.equals(mAntennaPositionMap, that.mAntennaPositionMap);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSupportedRadioTechnologies, mIsPointingRequired,
+ mMaxBytesPerOutgoingDatagram, mAntennaPositionMap);
+ }
+
/**
* @return The list of technologies supported by the satellite modem.
*/
@@ -141,6 +186,16 @@ public final class SatelliteCapabilities implements Parcelable {
return mMaxBytesPerOutgoingDatagram;
}
+ /**
+ * Antenna Position received from satellite modem which gives information about antenna
+ * direction to be used with satellite communication and suggested device hold positions.
+ * @return Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition
+ */
+ @NonNull
+ public Map<Integer, AntennaPosition> getAntennaPositionMap() {
+ return mAntennaPositionMap;
+ }
+
private void readFromParcel(Parcel in) {
mSupportedRadioTechnologies = new HashSet<>();
int numSupportedRadioTechnologies = in.readInt();
@@ -152,5 +207,14 @@ public final class SatelliteCapabilities implements Parcelable {
mIsPointingRequired = in.readBoolean();
mMaxBytesPerOutgoingDatagram = in.readInt();
+
+ mAntennaPositionMap = new HashMap<>();
+ int antennaPositionMapSize = in.readInt();
+ for (int i = 0; i < antennaPositionMapSize; i++) {
+ int key = in.readInt();
+ AntennaPosition antennaPosition = in.readParcelable(
+ AntennaPosition.class.getClassLoader(), AntennaPosition.class);
+ mAntennaPositionMap.put(key, antennaPosition);
+ }
}
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 20f9bc8bef05..5681ab266c17 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -334,6 +334,46 @@ public class SatelliteManager {
@Retention(RetentionPolicy.SOURCE)
public @interface NTRadioTechnology {}
+ /** Suggested device hold position is unknown. */
+ public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0;
+ /** User is suggested to hold the device in portrait mode. */
+ public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1;
+ /** User is suggested to hold the device in landscape mode with left hand. */
+ public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2;
+ /** User is suggested to hold the device in landscape mode with right hand. */
+ public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"DEVICE_HOLD_POSITION_"}, value = {
+ DEVICE_HOLD_POSITION_UNKNOWN,
+ DEVICE_HOLD_POSITION_PORTRAIT,
+ DEVICE_HOLD_POSITION_LANDSCAPE_LEFT,
+ DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceHoldPosition {}
+
+ /** Display mode is unknown. */
+ public static final int DISPLAY_MODE_UNKNOWN = 0;
+ /** Display mode of the device used for satellite communication for non-foldable phones. */
+ public static final int DISPLAY_MODE_FIXED = 1;
+ /** Display mode of the device used for satellite communication for foldabale phones when the
+ * device is opened. */
+ public static final int DISPLAY_MODE_OPENED = 2;
+ /** Display mode of the device used for satellite communication for foldabable phones when the
+ * device is closed. */
+ public static final int DISPLAY_MODE_CLOSED = 3;
+
+ /** @hide */
+ @IntDef(prefix = {"ANTENNA_POSITION_"}, value = {
+ DISPLAY_MODE_UNKNOWN,
+ DISPLAY_MODE_FIXED,
+ DISPLAY_MODE_OPENED,
+ DISPLAY_MODE_CLOSED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DisplayMode {}
+
/**
* Request to enable or disable the satellite modem and demo mode. If the satellite modem is
* enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl
index cd69da18c5b0..eaf96abeb80a 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteCapabilities.aidl
@@ -17,7 +17,7 @@
package android.telephony.satellite.stub;
import android.telephony.satellite.stub.NTRadioTechnology;
-
+import android.telephony.satellite.AntennaPosition;
/**
* {@hide}
*/
@@ -36,4 +36,14 @@ parcelable SatelliteCapabilities {
* The maximum number of bytes per datagram that can be sent over satellite.
*/
int maxBytesPerOutgoingDatagram;
+
+ /**
+ * Keys which are used to fill mAntennaPositionMap.
+ */
+ int[] antennaPositionKeys;
+
+ /**
+ * Antenna Position for different display modes received from satellite modem.
+ */
+ AntennaPosition[] antennaPositionValues;
}
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index f2ffc19f2a4e..7272abba897d 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -29,6 +29,7 @@
<option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
<option name="teardown-command" value="settings delete system show_touches" />
<option name="teardown-command" value="settings delete system pointer_location" />
+ <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index f8d885ae3faf..d7fa124623ce 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -417,9 +417,9 @@ public class PackageWatchdogTest {
int failureReason, int mitigationCount) {
if (versionedPackage.getVersionCode() == VERSION_CODE) {
// Only rollback for specific versionCode
- return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
}
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
};
@@ -442,13 +442,13 @@ public class PackageWatchdogTest {
public void testPackageFailureNotifyAllDifferentImpacts() throws Exception {
PackageWatchdog watchdog = createWatchdog();
TestObserver observerNone = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_NONE);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observerMid = new TestObserver(OBSERVER_NAME_3,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
TestObserver observerLow = new TestObserver(OBSERVER_NAME_4,
- PackageHealthObserverImpact.USER_IMPACT_LOW);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
@@ -499,9 +499,9 @@ public class PackageWatchdogTest {
public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception {
PackageWatchdog watchdog = createWatchdog();
TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LOW);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
@@ -517,7 +517,7 @@ public class PackageWatchdogTest {
assertThat(observerSecond.mMitigatedPackages).isEmpty();
// After observerFirst handles failure, next action it has is high impact
- observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_HIGH;
+ observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_100;
observerFirst.mMitigatedPackages.clear();
observerSecond.mMitigatedPackages.clear();
@@ -531,7 +531,7 @@ public class PackageWatchdogTest {
assertThat(observerFirst.mMitigatedPackages).isEmpty();
// After observerSecond handles failure, it has no further actions
- observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
+ observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
observerFirst.mMitigatedPackages.clear();
observerSecond.mMitigatedPackages.clear();
@@ -545,7 +545,7 @@ public class PackageWatchdogTest {
assertThat(observerSecond.mMitigatedPackages).isEmpty();
// After observerFirst handles failure, it too has no further actions
- observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
+ observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
observerFirst.mMitigatedPackages.clear();
observerSecond.mMitigatedPackages.clear();
@@ -566,9 +566,9 @@ public class PackageWatchdogTest {
public void testPackageFailureNotifyOneSameImpact() throws Exception {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing for observer1 and observer2 with failure handling
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
@@ -592,11 +592,11 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observer3 = new TestObserver(OBSERVER_NAME_3,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing with explicit health checks for APP_A and APP_B respectively
@@ -645,7 +645,7 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing with explicit health checks for APP_A and APP_B
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
@@ -711,7 +711,7 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing with explicit health checks for APP_A and
// package observation duration == LONG_DURATION
@@ -742,7 +742,7 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing with explicit health checks for APP_A and
// package observation duration == SHORT_DURATION / 2
@@ -818,7 +818,7 @@ public class PackageWatchdogTest {
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
@@ -1073,9 +1073,9 @@ public class PackageWatchdogTest {
public void testBootLoopMitigationDoneForLowestUserImpact() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1);
- bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LOW);
+ bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
- bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
watchdog.registerHealthObserver(bootObserver1);
watchdog.registerHealthObserver(bootObserver2);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
@@ -1446,7 +1446,7 @@ public class PackageWatchdogTest {
TestObserver(String name) {
mName = name;
- mImpact = PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+ mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
}
TestObserver(String name, int impact) {
diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml
index 2bfb04fdb765..1731f6be4bf2 100644
--- a/tests/testables/tests/AndroidManifest.xml
+++ b/tests/testables/tests/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
- <application android:debuggable="true">
+ <application android:debuggable="true" android:testOnly="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/testables/tests/AndroidTest.xml b/tests/testables/tests/AndroidTest.xml
new file mode 100644
index 000000000000..6d2979423efa
--- /dev/null
+++ b/tests/testables/tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs Testable Tests.">
+ <option name="test-tag" value="TestablesTests" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="TestablesTests.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.testables"/>
+ </test>
+</configuration>
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index a146466402f6..e2c161482857 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -49,6 +49,7 @@ enum {
SDK_S = 31,
SDK_S_V2 = 32,
SDK_TIRAMISU = 33,
+ SDK_UPSIDE_DOWN_CAKE = 34,
SDK_CUR_DEVELOPMENT = 10000,
};
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 40bcef787815..e47704ea2725 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -59,6 +59,7 @@ enum : ApiVersion {
SDK_S = 31,
SDK_S_V2 = 32,
SDK_TIRAMISU = 33,
+ SDK_UPSIDE_DOWN_CAKE = 34,
SDK_CUR_DEVELOPMENT = 10000,
};