summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp5
-rw-r--r--ApiDocs.bp3
-rw-r--r--StubLibraries.bp2
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java21
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java51
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java64
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java22
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java18
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java167
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java23
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java13
-rw-r--r--api/Android.bp8
-rw-r--r--boot/Android.bp4
-rw-r--r--config/README.md13
-rw-r--r--core/api/current.txt50
-rwxr-xr-xcore/api/system-current.txt17
-rw-r--r--core/api/system-removed.txt8
-rw-r--r--core/api/test-current.txt29
-rw-r--r--core/java/android/app/ActivityManager.java29
-rw-r--r--core/java/android/app/ActivityManagerInternal.java7
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/TEST_MAPPING9
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java8
-rw-r--r--core/java/android/companion/AssociationRequest.java19
-rw-r--r--core/java/android/content/pm/ActivityInfo.java69
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java2
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivityUtils.java15
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java13
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java11
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java11
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl3
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java27
-rw-r--r--core/java/android/net/IpSecAlgorithm.java9
-rw-r--r--core/java/android/os/Parcel.java216
-rw-r--r--core/java/android/os/health/HealthStats.java2
-rw-r--r--core/java/android/os/health/UidHealthStats.java6
-rw-r--r--core/java/android/provider/DeviceConfig.java8
-rw-r--r--core/java/android/provider/Settings.java36
-rw-r--r--core/java/android/util/TimingsTraceLog.java34
-rw-r--r--core/java/android/view/IRecentsAnimationRunner.aidl2
-rw-r--r--core/java/android/view/ViewRootImpl.java49
-rw-r--r--core/java/android/view/WindowManager.java19
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java65
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java30
-rw-r--r--core/java/android/view/inputmethod/TextAttribute.aidl19
-rw-r--r--core/java/android/view/inputmethod/TextAttribute.java140
-rw-r--r--core/java/android/view/inputmethod/TextSnapshot.java4
-rw-r--r--core/java/android/window/TaskFragmentInfo.java1
-rw-r--r--core/java/com/android/internal/compat/OWNERS7
-rw-r--r--core/java/com/android/internal/inputmethod/IInputContextInvoker.java67
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java52
-rw-r--r--core/java/com/android/internal/inputmethod/StartInputFlags.java10
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl12
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp6
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp11
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml85
-rw-r--r--core/res/res/values/attrs_manifest.xml3
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/public.xml5
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/PlatformCompatFramework/OWNERS7
-rw-r--r--data/etc/com.android.cellbroadcastreceiver.xml1
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java51
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java69
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java45
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin18462 -> 19183 bytes
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java114
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java31
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java94
-rw-r--r--libs/androidfw/ResourceTypes.cpp23
-rw-r--r--libs/androidfw/tests/Config_test.cpp3
-rw-r--r--libs/hwui/pipeline/skia/FunctorDrawable.h4
-rw-r--r--libs/hwui/pipeline/skia/TransformCanvas.cpp14
-rw-r--r--media/jni/tuner/DvrClient.cpp6
-rw-r--r--media/jni/tuner/FilterClient.cpp9
-rw-r--r--packages/SettingsLib/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml4
-rw-r--r--packages/SettingsLib/Utils/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java35
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java9
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java13
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java5
-rw-r--r--packages/SystemUI/OWNERS1
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt3
-rw-r--r--packages/SystemUI/plugin/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/proguard.flags6
-rw-r--r--packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml2
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf.xml6
-rw-r--r--packages/SystemUI/res/layout/notif_half_shelf_row.xml6
-rw-r--r--packages/SystemUI/res/values/colors.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java45
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java98
-rw-r--r--packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java39
-rw-r--r--packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/CoreStartable.java (renamed from packages/SystemUI/src/com/android/systemui/SystemUI.java)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/VendorServices.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java)25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java)14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java225
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java162
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java114
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java36
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java19
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java11
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java33
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java9
-rw-r--r--services/core/Android.bp8
-rw-r--r--services/core/java/com/android/server/BatteryService.java490
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java101
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java61
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java45
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/am/UserController.java48
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java8
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java3
-rw-r--r--services/core/java/com/android/server/biometrics/PreAuthInfo.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java9
-rw-r--r--services/core/java/com/android/server/compat/OWNERS7
-rw-r--r--services/core/java/com/android/server/health/HealthHalCallbackHidl.java117
-rw-r--r--services/core/java/com/android/server/health/HealthInfoCallback.java31
-rw-r--r--services/core/java/com/android/server/health/HealthServiceWrapper.java107
-rw-r--r--services/core/java/com/android/server/health/HealthServiceWrapperHidl.java313
-rw-r--r--services/core/java/com/android/server/health/Utils.java85
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java2
-rw-r--r--services/core/java/com/android/server/locales/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java6
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java33
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java7
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java3
-rw-r--r--services/core/java/com/android/server/net/OWNERS8
-rw-r--r--services/core/java/com/android/server/notification/CountdownConditionProvider.java17
-rw-r--r--services/core/java/com/android/server/notification/EventConditionProvider.java19
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java14
-rw-r--r--services/core/java/com/android/server/pm/InstallParams.java16
-rw-r--r--services/core/java/com/android/server/pm/Installer.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java108
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java4
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java714
-rw-r--r--services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java5
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java61
-rw-r--r--services/core/java/com/android/server/pm/ShortcutUser.java5
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java2
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java65
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java29
-rw-r--r--services/core/java/com/android/server/utils/TimingsTraceAndSlog.java8
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java27
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java2
-rw-r--r--services/core/java/com/android/server/wm/PackageConfigPersister.java6
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java7
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java51
-rw-r--r--services/core/java/com/android/server/wm/Task.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java228
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java7
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt89
-rw-r--r--services/tests/mockingservicestests/Android.bp7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java163
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java56
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java141
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java258
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java284
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java172
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java229
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java206
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java (renamed from services/tests/servicestests/src/com/android/server/BatteryServiceTest.java)96
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java289
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java223
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java73
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java73
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java46
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java49
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java6
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java24
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java26
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java10
-rw-r--r--telephony/java/android/telephony/AvailableNetworkInfo.java106
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java131
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java13
-rw-r--r--tests/FlickerTests/OWNERS3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt2
-rw-r--r--tools/aapt2/util/Files.cpp2
-rw-r--r--tools/aapt2/util/Files.h2
308 files changed, 6506 insertions, 4320 deletions
diff --git a/Android.bp b/Android.bp
index e1cb037b252e..82da1e63a481 100644
--- a/Android.bp
+++ b/Android.bp
@@ -154,6 +154,7 @@ java_library {
"framework-scheduling.stubs.module_lib",
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
+ "framework-supplementalprocess.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
@@ -177,6 +178,7 @@ java_library {
"framework-scheduling.impl",
"framework-sdkextensions.impl",
"framework-statsd.impl",
+ "framework-supplementalprocess.impl",
"framework-tethering.impl",
"framework-uwb.impl",
"framework-wifi.impl",
@@ -441,11 +443,8 @@ filegroup {
"core/java/android/util/LocalLog.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/IndentingPrintWriter.java",
- "core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
"core/java/com/android/internal/util/RingBufferIndices.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/com/android/internal/util/TokenBucket.java",
],
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 31f7f6e014c3..7a4ef2ab325a 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -126,6 +126,7 @@ stubs_defaults {
":framework-scheduling-sources",
":framework-sdkextensions-sources",
":framework-statsd-sources",
+ ":framework-supplementalprocess-sources",
":framework-tethering-srcs",
":framework-uwb-updatable-sources",
":framework-wifi-updatable-sources",
@@ -172,6 +173,7 @@ droidstubs {
":framework-scheduling{.public.stubs.source}",
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
+ ":framework-supplementalprocess{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
":framework-uwb{.public.stubs.source}",
":framework-wifi{.public.stubs.source}",
@@ -211,6 +213,7 @@ genrule {
":framework-scheduling{.public.annotations.zip}",
":framework-sdkextensions{.public.annotations.zip}",
":framework-statsd{.public.annotations.zip}",
+ ":framework-supplementalprocess{.public.annotations.zip}",
":framework-tethering{.public.annotations.zip}",
":framework-uwb{.public.annotations.zip}",
":framework-wifi{.public.annotations.zip}",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index cc118f32f8bf..b6fd708a6cce 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -250,6 +250,7 @@ modules_public_stubs = [
"framework-scheduling.stubs",
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
+ "framework-supplementalprocess.stubs",
"framework-tethering.stubs",
"framework-uwb.stubs",
"framework-wifi.stubs",
@@ -271,6 +272,7 @@ modules_system_stubs = [
"framework-scheduling.stubs.system",
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
+ "framework-supplementalprocess.stubs",
"framework-tethering.stubs.system",
"framework-uwb.stubs.system",
"framework-wifi.stubs.system",
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 1c40a06c1c86..42fb24f570d2 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -217,7 +217,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
}
@Override
- public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException {
+ public void onTasksAppeared(RemoteAnimationTarget[] app) throws RemoteException {
/* no-op */
}
};
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 31a5a050529b..392df732953f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -490,8 +490,8 @@ public class AlarmManagerService extends SystemService {
* holding the AlarmManagerService.mLock lock.
*/
@VisibleForTesting
- final class Constants extends ContentObserver
- implements DeviceConfig.OnPropertiesChangedListener {
+ final class Constants implements DeviceConfig.OnPropertiesChangedListener,
+ EconomyManagerInternal.TareStateChangeListener {
@VisibleForTesting
static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250;
@@ -695,7 +695,6 @@ public class AlarmManagerService extends SystemService {
private int mVersion = 0;
Constants(Handler handler) {
- super(handler);
updateAllowWhileIdleWhitelistDurationLocked();
for (int i = 0; i < APP_STANDBY_QUOTAS.length; i++) {
APP_STANDBY_QUOTAS[i] = DEFAULT_APP_STANDBY_QUOTAS[i];
@@ -709,11 +708,12 @@ public class AlarmManagerService extends SystemService {
}
public void start() {
- mInjector.registerContentObserver(this,
- Settings.Global.getUriFor(Settings.Global.ENABLE_TARE));
mInjector.registerDeviceConfigListener(this);
+ final EconomyManagerInternal economyManagerInternal =
+ LocalServices.getService(EconomyManagerInternal.class);
+ economyManagerInternal.registerTareStateChangeListener(this);
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
- updateTareSettings();
+ updateTareSettings(economyManagerInternal.isEnabled());
}
public void updateAllowWhileIdleWhitelistDurationLocked() {
@@ -886,15 +886,12 @@ public class AlarmManagerService extends SystemService {
}
@Override
- public void onChange(boolean selfChange) {
- updateTareSettings();
+ public void onTareEnabledStateChanged(boolean isTareEnabled) {
+ updateTareSettings(isTareEnabled);
}
- private void updateTareSettings() {
+ private void updateTareSettings(boolean isTareEnabled) {
synchronized (mLock) {
- final boolean isTareEnabled = Settings.Global.getInt(
- getContext().getContentResolver(),
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
if (USE_TARE_POLICY != isTareEnabled) {
USE_TARE_POLICY = isTareEnabled;
final boolean changed = mAlarmStore.updateAlarmDeliveries(alarm -> {
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 78140dce12f4..714c90b9b5a9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -43,7 +43,6 @@ import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -54,7 +53,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
@@ -118,6 +116,7 @@ import com.android.server.job.controllers.TimeController;
import com.android.server.job.restrictions.JobRestriction;
import com.android.server.job.restrictions.ThermalStatusRestriction;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomyManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import com.android.server.utils.quota.Categorizer;
@@ -356,41 +355,22 @@ public class JobSchedulerService extends com.android.server.SystemService
// (ScheduledJobStateChanged and JobStatusDumpProto).
public static final int RESTRICTED_INDEX = 5;
- private class ConstantsObserver extends ContentObserver
- implements DeviceConfig.OnPropertiesChangedListener {
- private final ContentResolver mContentResolver;
-
- ConstantsObserver(Handler handler, Context context) {
- super(handler);
- mContentResolver = context.getContentResolver();
- }
-
+ private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
+ EconomyManagerInternal.TareStateChangeListener {
public void start() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
JobSchedulerBackgroundThread.getExecutor(), this);
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
+ final EconomyManagerInternal economyManagerInternal =
+ LocalServices.getService(EconomyManagerInternal.class);
+ economyManagerInternal.registerTareStateChangeListener(this);
// Load all the constants.
synchronized (mLock) {
- mConstants.updateSettingsConstantsLocked(mContentResolver);
+ mConstants.updateTareSettingsLocked(economyManagerInternal.isEnabled());
}
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
}
@Override
- public void onChange(boolean selfChange) {
- synchronized (mLock) {
- if (mConstants.updateSettingsConstantsLocked(mContentResolver)) {
- for (int controller = 0; controller < mControllers.size(); controller++) {
- final StateController sc = mControllers.get(controller);
- sc.onConstantsUpdatedLocked();
- }
- onControllerStateChanged(null);
- }
- }
- }
-
- @Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
@@ -465,6 +445,17 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
}
+
+ @Override
+ public void onTareEnabledStateChanged(boolean isTareEnabled) {
+ if (mConstants.updateTareSettingsLocked(isTareEnabled)) {
+ for (int controller = 0; controller < mControllers.size(); controller++) {
+ final StateController sc = mControllers.get(controller);
+ sc.onConstantsUpdatedLocked();
+ }
+ onControllerStateChanged(null);
+ }
+ }
}
@VisibleForTesting
@@ -719,10 +710,8 @@ public class JobSchedulerService extends com.android.server.SystemService
DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
}
- private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) {
+ private boolean updateTareSettingsLocked(boolean isTareEnabled) {
boolean changed = false;
- final boolean isTareEnabled = Settings.Global.getInt(contentResolver,
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
if (USE_TARE_POLICY != isTareEnabled) {
USE_TARE_POLICY = isTareEnabled;
changed = true;
@@ -1673,7 +1662,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mHandler = new JobHandler(context.getMainLooper());
mConstants = new Constants();
- mConstantsObserver = new ConstantsObserver(mHandler, context);
+ mConstantsObserver = new ConstantsObserver();
mJobSchedulerStub = new JobSchedulerStub();
mConcurrencyManager = new JobConcurrencyManager(this);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index b2e95a58f0ec..c5f0f32a5aa0 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -16,8 +16,12 @@
package com.android.server.tare;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+
import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT;
+import static com.android.server.tare.EconomicPolicy.REGULATION_DEMOTION;
+import static com.android.server.tare.EconomicPolicy.REGULATION_PROMOTION;
import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION;
import static com.android.server.tare.EconomicPolicy.TYPE_ACTION;
import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
@@ -603,6 +607,51 @@ class Agent {
}
}
+ /**
+ * Reclaim a percentage of unused ARCs from an app that was just removed from an exemption list.
+ * The amount reclaimed will depend on how recently the app was used. The reclamation will not
+ * reduce an app's balance below its current minimum balance.
+ */
+ @GuardedBy("mLock")
+ void onAppUnexemptedLocked(final int userId, @NonNull final String pkgName) {
+ final long curBalance = getBalanceLocked(userId, pkgName);
+ final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ if (curBalance <= minBalance) {
+ return;
+ }
+ // AppStandby only counts elapsed time for things like this
+ // TODO: should we use clock time instead?
+ final long timeSinceLastUsedMs =
+ mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
+ // The app is no longer exempted. We should take away some of credits so it's more in line
+ // with other non-exempt apps. However, don't take away as many credits if the app was used
+ // recently.
+ final double percentageToReclaim;
+ if (timeSinceLastUsedMs < DAY_IN_MILLIS) {
+ percentageToReclaim = .25;
+ } else if (timeSinceLastUsedMs < 2 * DAY_IN_MILLIS) {
+ percentageToReclaim = .5;
+ } else if (timeSinceLastUsedMs < 3 * DAY_IN_MILLIS) {
+ percentageToReclaim = .75;
+ } else {
+ percentageToReclaim = 1;
+ }
+ final long overage = curBalance - minBalance;
+ final long toReclaim = (long) (overage * percentageToReclaim);
+ if (toReclaim > 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Reclaiming bonus wealth! Taking " + toReclaim
+ + " from " + appToString(userId, pkgName));
+ }
+
+ final long now = getCurrentTimeMillis();
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim),
+ true);
+ }
+ }
+
/** Returns true if an app should be given credits in the general distributions. */
private boolean shouldGiveCredits(@NonNull PackageInfo packageInfo) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
@@ -701,6 +750,21 @@ class Agent {
}
@GuardedBy("mLock")
+ void onAppExemptedLocked(final int userId, @NonNull final String pkgName) {
+ final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ final long missing = minBalance - getBalanceLocked(userId, pkgName);
+ if (missing <= 0) {
+ return;
+ }
+
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
+ final long now = getCurrentTimeMillis();
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing), true);
+ }
+
+ @GuardedBy("mLock")
void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) {
reclaimAssetsLocked(userId, pkgName);
mBalanceThresholdAlarmQueue.removeAlarmForKey(new Package(userId, pkgName));
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index 2b4938bc7746..d7290191dc3c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -35,6 +35,7 @@ import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WA
import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CIRCULATION;
import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP;
import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX;
@@ -71,6 +72,7 @@ import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP
import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
import static android.app.tare.EconomyManager.KEY_AM_MAX_CIRCULATION;
import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX;
@@ -138,7 +140,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
COST_MODIFIER_PROCESS_STATE
};
- private long mMinSatiatedBalance;
+ private long mMinSatiatedBalanceExempted;
+ private long mMinSatiatedBalanceOther;
private long mMaxSatiatedBalance;
private long mMaxSatiatedCirculation;
@@ -163,8 +166,11 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
@Override
long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
- // TODO: take exemption into account
- return mMinSatiatedBalance;
+ if (mInternalResourceService.isPackageExempted(userId, pkgName)) {
+ return mMinSatiatedBalanceExempted;
+ }
+ // TODO: take other exemptions into account
+ return mMinSatiatedBalanceOther;
}
@Override
@@ -205,7 +211,9 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
+ mMinSatiatedBalanceExempted = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
+ DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED));
+ mMinSatiatedBalanceOther = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
DEFAULT_AM_MAX_SATIATED_BALANCE));
@@ -343,7 +351,11 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
@Override
void dump(IndentingPrintWriter pw) {
- pw.print("Min satiated balance", narcToString(mMinSatiatedBalance)).println();
+ pw.println("Min satiated balances:");
+ pw.increaseIndent();
+ pw.print("Exempted", narcToString(mMinSatiatedBalanceExempted)).println();
+ pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
+ pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
pw.print("Min satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index d16e378bce9e..c1177b20bb0e 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -57,6 +57,8 @@ public abstract class EconomicPolicy {
static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0;
static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1;
static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2;
+ static final int REGULATION_PROMOTION = TYPE_REGULATION | 3;
+ static final int REGULATION_DEMOTION = TYPE_REGULATION | 4;
static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0;
static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1;
@@ -363,6 +365,10 @@ public abstract class EconomicPolicy {
return "BIRTHRIGHT";
case REGULATION_WEALTH_RECLAMATION:
return "WEALTH_RECLAMATION";
+ case REGULATION_PROMOTION:
+ return "PROMOTION";
+ case REGULATION_DEMOTION:
+ return "DEMOTION";
}
return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
index 29aa94631d68..630f1e7bfb92 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -119,6 +119,11 @@ public interface EconomyManagerInternal {
boolean canAfford);
}
+ /** Listener for various TARE state changes. */
+ interface TareStateChangeListener {
+ void onTareEnabledStateChanged(boolean isTareEnabled);
+ }
+
/**
* Return {@code true} if the app is able to pay for the anticipated actions.
*/
@@ -130,6 +135,9 @@ public interface EconomyManagerInternal {
*/
long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
+ /** Returns true if TARE is enabled. */
+ boolean isEnabled();
+
/**
* Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the
* indicated bill changes.
@@ -145,6 +153,16 @@ public interface EconomyManagerInternal {
@NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
/**
+ * Register a {@link TareStateChangeListener} to track when TARE's state changes.
+ */
+ void registerTareStateChangeListener(@NonNull TareStateChangeListener listener);
+
+ /**
+ * Unregister a {@link TareStateChangeListener} from being notified when TARE's state changes.
+ */
+ void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener);
+
+ /**
* Note that an instantaneous event has occurred. The event must be specified in one of the
* EconomicPolicies.
*
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 1cd873c675c0..437a10198d8e 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -45,10 +45,15 @@ import android.net.Uri;
import android.os.BatteryManagerInternal;
import android.os.Binder;
import android.os.Handler;
+import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -64,11 +69,13 @@ import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
@@ -105,6 +112,8 @@ public class InternalResourceService extends SystemService {
private final PackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
+ private IDeviceIdleController mDeviceIdleController;
+
private final Agent mAgent;
private final ConfigObserver mConfigObserver;
private final EconomyManagerStub mEconomyManagerStub;
@@ -163,8 +172,17 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mPackageToUidCache")
private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+ private final CopyOnWriteArraySet<TareStateChangeListener> mStateChangeListeners =
+ new CopyOnWriteArraySet<>();
+
+ /** List of packages that are "exempted" from battery restrictions. */
+ // TODO(144864180): include userID
+ @GuardedBy("mLock")
+ private ArraySet<String> mExemptedApps = new ArraySet<>();
+
private volatile boolean mIsEnabled;
private volatile int mBootPhase;
+ private volatile boolean mExemptListLoaded;
// In the range [0,100] to represent 0% to 100% battery.
@GuardedBy("mLock")
private int mCurrentBatteryLevel;
@@ -213,6 +231,9 @@ public class InternalResourceService extends SystemService {
onUserRemoved(userId);
}
break;
+ case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
+ onExemptionListChanged();
+ break;
}
}
};
@@ -246,6 +267,7 @@ public class InternalResourceService extends SystemService {
private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
private static final int MSG_PROCESS_USAGE_EVENT = 2;
private static final int MSG_MAYBE_FORCE_RECLAIM = 3;
+ private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
/**
@@ -285,7 +307,22 @@ public class InternalResourceService extends SystemService {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
mConfigObserver.start();
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
setupEverything();
+ } else if (PHASE_BOOT_COMPLETED == phase) {
+ if (!mExemptListLoaded) {
+ synchronized (mLock) {
+ try {
+ mExemptedApps =
+ new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, e);
+ }
+ mExemptListLoaded = true;
+ }
+ }
}
}
@@ -350,6 +387,12 @@ public class InternalResourceService extends SystemService {
return mIsEnabled;
}
+ boolean isPackageExempted(final int userId, @NonNull String pkgName) {
+ synchronized (mLock) {
+ return mExemptedApps.contains(pkgName);
+ }
+ }
+
boolean isSystem(final int userId, @NonNull String pkgName) {
if ("android".equals(pkgName)) {
return true;
@@ -375,6 +418,53 @@ public class InternalResourceService extends SystemService {
}
}
+ void onExemptionListChanged() {
+ final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds();
+ synchronized (mLock) {
+ final ArraySet<String> removed = mExemptedApps;
+ final ArraySet<String> added = new ArraySet<>();
+ try {
+ mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, e);
+ return;
+ }
+
+ for (int i = mExemptedApps.size() - 1; i >= 0; --i) {
+ final String pkg = mExemptedApps.valueAt(i);
+ if (!removed.contains(pkg)) {
+ added.add(pkg);
+ }
+ removed.remove(pkg);
+ }
+ for (int a = added.size() - 1; a >= 0; --a) {
+ final String pkgName = added.valueAt(a);
+ for (int userId : userIds) {
+ // Since the exemption list doesn't specify user ID and we track by user ID,
+ // we need to see if the app exists on the user before talking to the agent.
+ // Otherwise, we may end up with invalid ledgers.
+ final boolean appExists = getUid(userId, pkgName) >= 0;
+ if (appExists) {
+ mAgent.onAppExemptedLocked(userId, pkgName);
+ }
+ }
+ }
+ for (int r = removed.size() - 1; r >= 0; --r) {
+ final String pkgName = removed.valueAt(r);
+ for (int userId : userIds) {
+ // Since the exemption list doesn't specify user ID and we track by user ID,
+ // we need to see if the app exists on the user before talking to the agent.
+ // Otherwise, we may end up with invalid ledgers.
+ final boolean appExists = getUid(userId, pkgName) >= 0;
+ if (appExists) {
+ mAgent.onAppUnexemptedLocked(userId, pkgName);
+ }
+ }
+ }
+ }
+ }
+
void onPackageAdded(final int uid, @NonNull final String pkgName) {
final int userId = UserHandle.getUserId(uid);
final PackageInfo packageInfo;
@@ -602,6 +692,7 @@ public class InternalResourceService extends SystemService {
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
final IntentFilter pkgFilter = new IntentFilter();
@@ -625,6 +716,15 @@ public class InternalResourceService extends SystemService {
private void setupHeavyWork() {
synchronized (mLock) {
loadInstalledPackageListLocked();
+ if (mBootPhase >= PHASE_BOOT_COMPLETED && !mExemptListLoaded) {
+ try {
+ mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, e);
+ }
+ mExemptListLoaded = true;
+ }
final boolean isFirstSetup = !mScribe.recordExists();
if (isFirstSetup) {
mAgent.grantBirthrightsLocked();
@@ -654,6 +754,8 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
mAgent.tearDownLocked();
mCompleteEconomicPolicy.tearDown();
+ mExemptedApps.clear();
+ mExemptListLoaded = false;
mHandler.post(() -> {
// Never call out to AlarmManager with the lock held. This sits below AM.
AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
@@ -707,6 +809,13 @@ public class InternalResourceService extends SystemService {
}
break;
+ case MSG_NOTIFY_STATE_CHANGE_LISTENERS: {
+ for (TareStateChangeListener listener : mStateChangeListeners) {
+ listener.onTareEnabledStateChanged(mIsEnabled);
+ }
+ }
+ break;
+
case MSG_PROCESS_USAGE_EVENT: {
final int userId = msg.arg1;
final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
@@ -797,6 +906,16 @@ public class InternalResourceService extends SystemService {
}
@Override
+ public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener) {
+ mStateChangeListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) {
+ mStateChangeListeners.remove(listener);
+ }
+
+ @Override
public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
if (!mIsEnabled) {
return true;
@@ -849,6 +968,11 @@ public class InternalResourceService extends SystemService {
}
@Override
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ @Override
public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
@Nullable String tag) {
if (!mIsEnabled) {
@@ -885,7 +1009,10 @@ public class InternalResourceService extends SystemService {
}
}
- private class ConfigObserver extends ContentObserver {
+ private class ConfigObserver extends ContentObserver
+ implements DeviceConfig.OnPropertiesChangedListener {
+ private static final String KEY_DC_ENABLE_TARE = "enable_tare";
+
private final ContentResolver mContentResolver;
ConfigObserver(Handler handler, Context context) {
@@ -894,12 +1021,15 @@ public class InternalResourceService extends SystemService {
}
public void start() {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_TARE,
+ TareHandlerThread.getExecutor(), this);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
+ onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE));
updateEnabledStatus();
}
@@ -913,9 +1043,31 @@ public class InternalResourceService extends SystemService {
}
}
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ synchronized (mLock) {
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
+ switch (name) {
+ case KEY_DC_ENABLE_TARE:
+ updateEnabledStatus();
+ break;
+ }
+ }
+ }
+ }
+
private void updateEnabledStatus() {
+ // User setting should override DeviceConfig setting.
+ // NOTE: There's currently no way for a user to reset the value (via UI), so if a user
+ // manually toggles TARE via UI, we'll always defer to the user's current setting
+ // TODO: add a "reset" value if the user toggle is an issue
+ final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE,
+ KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1);
final boolean isTareEnabled = Settings.Global.getInt(mContentResolver,
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
+ Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1;
if (mIsEnabled != isTareEnabled) {
mIsEnabled = isTareEnabled;
if (mIsEnabled) {
@@ -923,6 +1075,7 @@ public class InternalResourceService extends SystemService {
} else {
tearDownEverything();
}
+ mHandler.sendEmptyMessage(MSG_NOTIFY_STATE_CHANGE_LISTENERS);
}
}
@@ -953,9 +1106,9 @@ public class InternalResourceService extends SystemService {
pw.print("Current battery level: ");
pw.println(mCurrentBatteryLevel);
- final long maxCircluation = getMaxCirculationLocked();
+ final long maxCirculation = getMaxCirculationLocked();
pw.print("Max circulation (current/satiated): ");
- pw.print(narcToString(maxCircluation));
+ pw.print(narcToString(maxCirculation));
pw.print("/");
pw.println(narcToString(mCompleteEconomicPolicy.getMaxSatiatedCirculation()));
@@ -963,10 +1116,14 @@ public class InternalResourceService extends SystemService {
pw.print("Current GDP: ");
pw.print(narcToString(currentCirculation));
pw.print(" (");
- pw.print(String.format("%.2f", 100f * currentCirculation / maxCircluation));
+ pw.print(String.format("%.2f", 100f * currentCirculation / maxCirculation));
pw.println("% of current max)");
pw.println();
+ pw.print("Exempted apps", mExemptedApps);
+ pw.println();
+
+ pw.println();
mCompleteEconomicPolicy.dump(pw);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 728f5e80d4ca..2318026d9f16 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -40,6 +40,7 @@ import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENA
import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CIRCULATION;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
@@ -80,6 +81,7 @@ import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_
import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
import static android.app.tare.EconomyManager.KEY_JS_MAX_CIRCULATION;
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
@@ -140,7 +142,8 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
COST_MODIFIER_PROCESS_STATE
};
- private long mMinSatiatedBalance;
+ private long mMinSatiatedBalanceExempted;
+ private long mMinSatiatedBalanceOther;
private long mMaxSatiatedBalance;
private long mMaxSatiatedCirculation;
@@ -165,8 +168,11 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
@Override
long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
- // TODO: incorporate time since usage
- return mMinSatiatedBalance;
+ if (mInternalResourceService.isPackageExempted(userId, pkgName)) {
+ return mMinSatiatedBalanceExempted;
+ }
+ // TODO: take other exemptions into account
+ return mMinSatiatedBalanceOther;
}
@Override
@@ -207,7 +213,10 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
Slog.e(TAG, "Global setting key incorrect: ", e);
}
- mMinSatiatedBalance = arcToNarc(
+ mMinSatiatedBalanceExempted = arcToNarc(
+ mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
+ DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED));
+ mMinSatiatedBalanceOther = arcToNarc(
mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
@@ -317,7 +326,11 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
@Override
void dump(IndentingPrintWriter pw) {
- pw.print("Min satiated balance", narcToString(mMinSatiatedBalance)).println();
+ pw.println("Min satiated balances:");
+ pw.increaseIndent();
+ pw.print("Exempted", narcToString(mMinSatiatedBalanceExempted)).println();
+ pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
+ pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
pw.print("Min satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
index 5b2b6607ba14..65ef8bfe0d06 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -17,10 +17,13 @@
package com.android.server.tare;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Trace;
+import java.util.concurrent.Executor;
+
/**
* Singleton thread for all of TARE.
*
@@ -29,6 +32,7 @@ import android.os.Trace;
final class TareHandlerThread extends HandlerThread {
private static TareHandlerThread sInstance;
+ private static Executor sHandlerExecutor;
private static Handler sHandler;
private TareHandlerThread() {
@@ -42,6 +46,7 @@ final class TareHandlerThread extends HandlerThread {
final Looper looper = sInstance.getLooper();
looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
sHandler = new Handler(sInstance.getLooper());
+ sHandlerExecutor = new HandlerExecutor(sHandler);
}
}
@@ -52,6 +57,14 @@ final class TareHandlerThread extends HandlerThread {
return sInstance;
}
+ /** Returns the singleton handler executor for TareHandlerThread */
+ public static Executor getExecutor() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ return sHandlerExecutor;
+ }
+ }
+
/** Returns the singleton handler for TareHandlerThread. */
public static Handler getHandler() {
synchronized (TareHandlerThread.class) {
diff --git a/api/Android.bp b/api/Android.bp
index 1bc50bd409a2..1ec1b3c6ba93 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -106,6 +106,7 @@ genrule {
":framework-scheduling{.public.api.txt}",
":framework-sdkextensions{.public.api.txt}",
":framework-statsd{.public.api.txt}",
+ ":framework-supplementalprocess{.public.api.txt}",
":framework-tethering{.public.api.txt}",
":framework-uwb{.public.api.txt}",
":framework-wifi{.public.api.txt}",
@@ -167,6 +168,7 @@ genrule {
":framework-scheduling{.public.stubs.source}",
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
+ ":framework-supplementalprocess{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
":framework-uwb{.public.stubs.source}",
":framework-wifi{.public.stubs.source}",
@@ -195,6 +197,7 @@ genrule {
":framework-scheduling{.public.removed-api.txt}",
":framework-sdkextensions{.public.removed-api.txt}",
":framework-statsd{.public.removed-api.txt}",
+ ":framework-supplementalprocess{.public.removed-api.txt}",
":framework-tethering{.public.removed-api.txt}",
":framework-uwb{.public.removed-api.txt}",
":framework-wifi{.public.removed-api.txt}",
@@ -237,6 +240,7 @@ genrule {
":framework-scheduling{.system.api.txt}",
":framework-sdkextensions{.system.api.txt}",
":framework-statsd{.system.api.txt}",
+ ":framework-supplementalprocess{.system.api.txt}",
":framework-tethering{.system.api.txt}",
":framework-uwb{.system.api.txt}",
":framework-wifi{.system.api.txt}",
@@ -297,6 +301,7 @@ genrule {
":framework-scheduling{.system.removed-api.txt}",
":framework-sdkextensions{.system.removed-api.txt}",
":framework-statsd{.system.removed-api.txt}",
+ ":framework-supplementalprocess{.system.removed-api.txt}",
":framework-tethering{.system.removed-api.txt}",
":framework-uwb{.system.removed-api.txt}",
":framework-wifi{.system.removed-api.txt}",
@@ -339,6 +344,7 @@ genrule {
":framework-scheduling{.module-lib.api.txt}",
":framework-sdkextensions{.module-lib.api.txt}",
":framework-statsd{.module-lib.api.txt}",
+ ":framework-supplementalprocess{.module-lib.api.txt}",
":framework-tethering{.module-lib.api.txt}",
":framework-uwb{.module-lib.api.txt}",
":framework-wifi{.module-lib.api.txt}",
@@ -401,6 +407,7 @@ genrule {
":framework-scheduling{.module-lib.removed-api.txt}",
":framework-sdkextensions{.module-lib.removed-api.txt}",
":framework-statsd{.module-lib.removed-api.txt}",
+ ":framework-supplementalprocess{.module-lib.removed-api.txt}",
":framework-tethering{.module-lib.removed-api.txt}",
":framework-uwb{.module-lib.removed-api.txt}",
":framework-wifi{.module-lib.removed-api.txt}",
@@ -521,6 +528,7 @@ genrule {
":framework-scheduling.stubs{.jar}",
":framework-sdkextensions.stubs{.jar}",
":framework-statsd.stubs{.jar}",
+ ":framework-supplementalprocess.stubs{.jar}",
":framework-tethering.stubs{.jar}",
":framework-uwb.stubs{.jar}",
":framework-wifi.stubs{.jar}",
diff --git a/boot/Android.bp b/boot/Android.bp
index d88e839cc79d..3273f2c6ed8e 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -96,6 +96,10 @@ platform_bootclasspath {
module: "com.android.sdkext-bootclasspath-fragment",
},
{
+ apex: "com.android.supplementalprocess",
+ module: "com.android.supplementalprocess-bootclasspath-fragment",
+ },
+ {
apex: "com.android.tethering",
module: "com.android.tethering-bootclasspath-fragment",
},
diff --git a/config/README.md b/config/README.md
new file mode 100644
index 000000000000..450a5c695c82
--- /dev/null
+++ b/config/README.md
@@ -0,0 +1,13 @@
+# Configuration files for ART compiling the framework
+
+* boot-image-profile.txt: A list of methods from the boot classpath to be compiled by dex2oat.
+ The order in the file is not relevant.
+* boot-profile.txt: An ordered list of methods from the boot classpath to be compiled by
+ the JIT in the order provided in the file. Used by JIT zygote, when on-device
+ signing failed.
+* dirty-image-objects: List of objects in the boot image which are known to
+ become dirty. This helps binning objects in the image file.
+* preloaded-classes: classes that will be allocated in the boot image, and
+ initialized by the zygote.
+* preloaded-classes-denylist: Classes that should not be initialized in the
+ zygote, as they have app-specific behavior.
diff --git a/core/api/current.txt b/core/api/current.txt
index 7370af382b14..5656c31c330a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -411,6 +411,7 @@ package android {
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
field public static final int canControlMagnification = 16844039; // 0x1010507
+ field public static final int canDisplayOnRemoteDevices;
field public static final int canPauseRecording = 16844314; // 0x101061a
field public static final int canPerformGestures = 16844045; // 0x101050d
field public static final int canRecord = 16844060; // 0x101051c
@@ -31636,6 +31637,8 @@ package android.os {
method @Nullable public double[] createDoubleArray();
method @Nullable public float[] createFloatArray();
method @Nullable public int[] createIntArray();
+ method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>);
+ method @Nullable public <T extends android.os.IInterface> java.util.ArrayList<T> createInterfaceArrayList(@NonNull java.util.function.Function<android.os.IBinder,T>);
method @Nullable public long[] createLongArray();
method @Nullable public String[] createStringArray();
method @Nullable public java.util.ArrayList<java.lang.String> createStringArrayList();
@@ -31674,13 +31677,17 @@ package android.os {
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
+ method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>);
method public int readInt();
method public void readIntArray(@NonNull int[]);
+ method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>);
+ method public <T extends android.os.IInterface> void readInterfaceList(@NonNull java.util.List<T>, @NonNull java.util.function.Function<android.os.IBinder,T>);
method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
method public long readLong();
method public void readLongArray(@NonNull long[]);
method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
+ method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
@@ -31728,6 +31735,8 @@ package android.os {
method public void writeFloatArray(@Nullable float[]);
method public void writeInt(int);
method public void writeIntArray(@Nullable int[]);
+ method public <T extends android.os.IInterface> void writeInterfaceArray(@Nullable T[]);
+ method public <T extends android.os.IInterface> void writeInterfaceList(@Nullable java.util.List<T>);
method public void writeInterfaceToken(@NonNull String);
method public void writeList(@Nullable java.util.List);
method public void writeLong(long);
@@ -40931,6 +40940,7 @@ package android.telephony {
method @NonNull public java.util.List<java.lang.Integer> getBands();
method @NonNull public java.util.List<java.lang.String> getMccMncs();
method public int getPriority();
+ method @NonNull public java.util.List<android.telephony.RadioAccessSpecifier> getRadioAccessSpecifiers();
method public int getSubId();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
@@ -40939,6 +40949,14 @@ package android.telephony {
field public static final int PRIORITY_MED = 2; // 0x2
}
+ public static final class AvailableNetworkInfo.Builder {
+ ctor public AvailableNetworkInfo.Builder(int);
+ method @NonNull public android.telephony.AvailableNetworkInfo build();
+ method @NonNull public android.telephony.AvailableNetworkInfo.Builder setMccMncs(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.telephony.AvailableNetworkInfo.Builder setPriority(int);
+ method @NonNull public android.telephony.AvailableNetworkInfo.Builder setRadioAccessSpecifiers(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
+ }
+
public final class BarringInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
@@ -40972,11 +40990,11 @@ package android.telephony {
}
public class CarrierConfigManager {
- method @Nullable public android.os.PersistableBundle getConfig();
- method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int);
- method @Nullable public android.os.PersistableBundle getConfigForSubId(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfig();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfigForSubId(int);
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
- method public void notifyConfigChangedForSubId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int);
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
@@ -41047,6 +41065,7 @@ package android.telephony {
field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
@@ -41108,6 +41127,8 @@ package android.telephony {
field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
+ field public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT = "esim_download_retry_backoff_timer_sec_int";
+ field public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT = "esim_max_download_retry_attempts_int";
field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -41210,6 +41231,7 @@ package android.telephony {
field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
@@ -42985,7 +43007,7 @@ package android.telephony {
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isModemEnabledForSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported();
method public boolean isNetworkRoaming();
method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
@@ -52250,6 +52272,7 @@ package android.view.inputmethod {
method public boolean commitContent(@NonNull android.view.inputmethod.InputContentInfo, int, @Nullable android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(CharSequence, int);
+ method public default boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean deleteSurroundingText(int, int);
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
@@ -52269,7 +52292,9 @@ package android.view.inputmethod {
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
+ method public default boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean setComposingText(CharSequence, int);
+ method public default boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public default boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
@@ -52485,6 +52510,21 @@ package android.view.inputmethod {
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
+ public final class TextAttribute implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getExtras();
+ method @NonNull public java.util.List<java.lang.String> getTextConversionSuggestions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextAttribute> CREATOR;
+ }
+
+ public static final class TextAttribute.TextAttributeBuilder {
+ ctor public TextAttribute.TextAttributeBuilder();
+ method @NonNull public android.view.inputmethod.TextAttribute build();
+ method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setExtras(@NonNull android.os.PersistableBundle);
+ method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setTextConversionSuggestions(@NonNull java.util.List<java.lang.String>);
+ }
+
public final class TextSnapshot {
ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
method @IntRange(from=0xffffffff) public int getCompositionEnd();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 27b2b2084f9c..d0a73bd3064e 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14,6 +14,7 @@ package android {
field public static final String ACCESS_MTP = "android.permission.ACCESS_MTP";
field public static final String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
+ field public static final String ACCESS_PDB_STATE = "android.permission.ACCESS_PDB_STATE";
field public static final String ACCESS_RCS_USER_CAPABILITY_EXCHANGE = "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE";
field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES";
field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
@@ -85,6 +86,7 @@ package android {
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+ field public static final String CONFIGURE_INTERACT_ACROSS_PROFILES = "android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES";
field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
@@ -107,6 +109,7 @@ package android {
field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
+ field public static final String GET_HISTORICAL_APP_OPS_STATS = "android.permission.GET_HISTORICAL_APP_OPS_STATS";
field public static final String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS";
field public static final String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
@@ -120,6 +123,7 @@ package android {
field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
field public static final String INSTALL_DPC_PACKAGES = "android.permission.INSTALL_DPC_PACKAGES";
field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
+ field public static final String INSTALL_EXISTING_PACKAGES = "com.android.permission.INSTALL_EXISTING_PACKAGES";
field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE";
field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
@@ -146,12 +150,14 @@ package android {
field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+ field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
+ field public static final String MANAGE_PROFILE_AND_DEVICE_OWNERS = "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS";
field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
@@ -168,6 +174,7 @@ package android {
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
+ field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
field public static final String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
field public static final String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
@@ -246,6 +253,7 @@ package android {
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
field public static final String REQUEST_COMPANION_PROFILE_APP_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING";
+ field public static final String REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION = "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION";
field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -299,6 +307,7 @@ package android {
field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
+ field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
@@ -368,6 +377,7 @@ package android {
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultSms = 17039396; // 0x1040024
+ field public static final int config_deviceManager;
field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d
@@ -948,7 +958,7 @@ package android.app.admin {
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
method @Nullable public CharSequence getDeviceOwnerOrganizationName();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
@@ -966,7 +976,7 @@ package android.app.admin {
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
- method @Deprecated @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
@@ -2333,6 +2343,7 @@ package android.companion {
public final class AssociationRequest implements android.os.Parcelable {
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING) public static final String DEVICE_PROFILE_APP_STREAMING = "android.app.role.COMPANION_DEVICE_APP_STREAMING";
+ field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION) public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
}
public final class CompanionDeviceManager {
@@ -10461,7 +10472,7 @@ package android.service.oemlock {
package android.service.persistentdata {
public class PersistentDataBlockManager {
- method @RequiresPermission("android.permission.ACCESS_PDB_STATE") public int getDataBlockSize();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize();
method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
method public long getMaximumDataBlockSize();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 9a8a49397322..9cb9ddcc6280 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -1,12 +1,4 @@
// Signature format: 2.0
-package android {
-
- public static final class Manifest.permission {
- field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
- }
-
-}
-
package android.app {
public class AppOpsManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 60a833a9d95f..5f87630fc11e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -114,7 +114,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopBackgroundUsersOnSwitch(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopUserOnSwitch(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
@@ -129,9 +129,9 @@ package android.app {
field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
field public static final int PROCESS_STATE_TOP = 2; // 0x2
- field public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1; // 0xffffffff
- field public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0; // 0x0
- field public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1; // 0x1
+ field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff
+ field public static final int STOP_USER_ON_SWITCH_FALSE = 0; // 0x0
+ field public static final int STOP_USER_ON_SWITCH_TRUE = 1; // 0x1
}
public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -442,11 +442,11 @@ package android.app.admin {
public class DevicePolicyManager {
method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void clearOrganizationId();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
@@ -455,18 +455,18 @@ package android.app.admin {
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
- method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
- method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
- method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
- method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
@@ -799,6 +799,7 @@ package android.content.pm {
field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d77227bdcf7c..d7ce3b8b6e1a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4087,45 +4087,46 @@ public class ActivityManager {
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1;
+ public static final int STOP_USER_ON_SWITCH_DEFAULT = -1;
/**
- * Overrides value defined by the platform and stop background users on switch.
+ * Overrides value defined by the platform and stop user on switch.
*
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1;
+ public static final int STOP_USER_ON_SWITCH_TRUE = 1;
/**
- * Overrides value defined by the platform and don't stop background users on switch.
+ * Overrides value defined by the platform and don't stop user on switch.
*
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0;
+ public static final int STOP_USER_ON_SWITCH_FALSE = 0;
/** @hide */
- @IntDef(prefix = { "STOP_BG_USERS_ON_SWITCH_" }, value = {
- STOP_BG_USERS_ON_SWITCH_DEFAULT,
- STOP_BG_USERS_ON_SWITCH_TRUE,
- STOP_BG_USERS_ON_SWITCH_FALSE
+ @IntDef(prefix = { "STOP_USER_ON_SWITCH_" }, value = {
+ STOP_USER_ON_SWITCH_DEFAULT,
+ STOP_USER_ON_SWITCH_TRUE,
+ STOP_USER_ON_SWITCH_FALSE
})
- public @interface StopBgUsersOnSwitch {}
+ public @interface StopUserOnSwitch {}
/**
- * Sets whether background users should be stopped when the current user is switched.
+ * Sets whether the current foreground user (and its profiles) should be stopped after switched
+ * out.
*
- * <p>Should only be used on tests.
+ * <p>Should only be used on tests. Doesn't apply to {@link UserHandle#SYSTEM system user}.
*
* @hide
*/
@TestApi
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
- public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
try {
- getService().setStopBackgroundUsersOnSwitch(value);
+ getService().setStopUserOnSwitch(value);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7dbcd5f3ffb2..324e1aea81e7 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,7 +16,7 @@
package android.app;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.StopUserOnSwitch;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -688,9 +688,10 @@ public abstract class ActivityManagerInternal {
@Nullable VoiceInteractionManagerProvider provider);
/**
- * Sets whether background users should be stopped when the current user is switched.
+ * Sets whether the current foreground user (and its profiles) should be stopped after switched
+ * out.
*/
- public abstract void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value);
+ public abstract void setStopUserOnSwitch(@StopUserOnSwitch int value);
/**
* Provides the interface to communicate between voice interaction manager service and
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 4b343270eb54..853d5e83abad 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -352,7 +352,7 @@ interface IActivityManager {
@UnsupportedAppUsage
boolean switchUser(int userid);
@UnsupportedAppUsage
- void setStopBackgroundUsersOnSwitch(int value);
+ void setStopUserOnSwitch(int value);
boolean removeTask(int taskId);
@UnsupportedAppUsage
void registerProcessObserver(in IProcessObserver observer);
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 31c81bef7357..32207af22dc1 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -146,6 +146,15 @@
}
],
"file_patterns": ["(/|^)ContextImpl.java"]
+ },
+ {
+ "file_patterns": ["(/|^)LocaleManager.java"],
+ "name": "CtsLocaleManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"presubmit-large": [
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8e8c035187d0..9045147f4bdd 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
@@ -77,6 +78,13 @@ public abstract class DevicePolicyManagerInternal {
OnCrossProfileWidgetProvidersChangeListener listener);
/**
+ * @param userHandle the handle of the user whose profile owner is being fetched.
+ * @return the configured supervision app if it exists and is the device owner or policy owner.
+ */
+ public abstract @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(
+ @NonNull UserHandle userHandle);
+
+ /**
* Checks if an app with given uid is an active device owner of its user.
*
* <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 24d124855508..7d1aabc639d9 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -94,6 +94,21 @@ public final class AssociationRequest implements Parcelable {
public static final String DEVICE_PROFILE_APP_STREAMING =
"android.app.role.COMPANION_DEVICE_APP_STREAMING";
+ /**
+ * Device profile: Android Automotive Projection
+ *
+ * Only applications that have been granted
+ * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION} are
+ * allowed to request to be associated with such devices.
+ *
+ * @see AssociationRequest.Builder#setDeviceProfile
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION)
+ @SystemApi
+ public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION =
+ "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
+
/** @hide */
@StringDef(value = { DEVICE_PROFILE_WATCH })
public @interface DeviceProfile {}
@@ -498,10 +513,10 @@ public final class AssociationRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1634716126923L,
+ time = 1635190605212L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
+ inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e7ca76e23368..1bc930a25070 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -448,6 +448,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @see android.app.Activity#setVrModeEnabled(boolean, ComponentName)
*/
public static final int FLAG_ENABLE_VR_MODE = 0x8000;
+ /**
+ * Bit in {@link #flags} indicating if the activity can be displayed on a remote device.
+ * Corresponds to {@link android.R.attr#canDisplayOnRemoteDevices}
+ * @hide
+ */
+ public static final int FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES = 0x10000;
/**
* Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
@@ -996,10 +1002,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
* OVERRIDE_MIN_ASPECT_RATIO_LARGE
*
- * If OVERRIDE_MIN_ASPECT_RATIO is applied, and the activity's orientation is fixed to
- * portrait, the min aspect ratio given in the app's manifest will be overridden to the
- * largest enabled aspect ratio treatment unless the app's manifest value is higher.
- * TODO(b/203647190): add OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY instead of portrait by default
+ * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's manifest
+ * will be overridden to the largest enabled aspect ratio treatment unless the app's manifest
+ * value is higher.
* @hide
*/
@ChangeId
@@ -1009,6 +1014,19 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
/**
+ * This change id restricts treatments that force a given min aspect ratio to activities
+ * whose orientation is fixed to portrait.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
+
+ /**
* This change id sets the activity's min aspect ratio to a medium value as defined by
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
*
@@ -1337,9 +1355,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
@SizeChangesSupportMode
public int supportsSizeChanges() {
- if (CompatChanges.isChangeEnabled(FORCE_NON_RESIZE_APP,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(FORCE_NON_RESIZE_APP)) {
return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
}
@@ -1347,9 +1363,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
return SIZE_CHANGES_SUPPORTED_METADATA;
}
- if (CompatChanges.isChangeEnabled(FORCE_RESIZE_APP,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(FORCE_RESIZE_APP)) {
return SIZE_CHANGES_SUPPORTED_OVERRIDE;
}
@@ -1361,9 +1375,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @hide
*/
public boolean neverSandboxDisplayApis() {
- return CompatChanges.isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
+ return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
}
@@ -1372,9 +1384,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @hide
*/
public boolean alwaysSandboxDisplayApis() {
- return CompatChanges.isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
+ return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
}
@@ -1404,31 +1414,28 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @hide
*/
public float getMinAspectRatio(@ScreenOrientation int orientation) {
- // TODO(b/203647190): check orientation only if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY
- // In case the activity's orientation isn't fixed to portrait, OVERRIDE_MIN_ASPECT_RATIO
- // shouldn't be applied.
- if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
- || !isFixedOrientationPortrait(orientation)) {
+ if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
+ isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !isFixedOrientationPortrait(orientation))) {
return mMinAspectRatio;
}
- if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
}
- if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
}
return mMinAspectRatio;
}
+ private boolean isChangeEnabled(long changeId) {
+ return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid));
+ }
+
/** @hide */
public float getManifestMinAspectRatio() {
return mMinAspectRatio;
@@ -1496,9 +1503,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* @hide
*/
public boolean shouldCheckMinWidthHeightForMultiWindow() {
- return CompatChanges.isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid));
+ return isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW);
}
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0a10aaaa2060..e3a5de514b22 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -194,6 +194,8 @@ public class ParsingPackageUtils {
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
+ public static final String METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES =
+ "android.can_display_on_remote_devices";
public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
"android.activity_window_layout_affinity";
public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 45241b0cb280..2ddf923d7dd9 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -420,6 +420,21 @@ public class ParsedActivityUtils {
}
}
+ if (!isAlias) {
+ // Default allow the activity to be displayed on a remote device unless it explicitly
+ // set to false.
+ boolean canDisplayOnRemoteDevices = array.getBoolean(
+ R.styleable.AndroidManifestActivity_canDisplayOnRemoteDevices, true);
+ if (activity.getMetaData() != null && !activity.getMetaData().getBoolean(
+ ParsingPackageUtils.METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES, true)) {
+ canDisplayOnRemoteDevices = false;
+ }
+ if (canDisplayOnRemoteDevices) {
+ activity.setFlags(activity.getFlags()
+ | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES);
+ }
+ }
+
ParseResult<ActivityInfo.WindowLayout> layoutResult =
resolveActivityWindowLayout(activity, input);
if (layoutResult.isError()) {
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c8c122da4ab8..6b5bec99e674 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -408,6 +408,19 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
+ * Flag to decide if authentication should ignore enrollment state.
+ * Defaults to false (not ignoring enrollment state)
+ * @param ignoreEnrollmentState
+ * @return This builder.
+ * @hide
+ */
+ @NonNull
+ public Builder setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+ mPromptInfo.setIgnoreEnrollmentState(ignoreEnrollmentState);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 339c654f4d2f..e6b762a64384 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -45,6 +45,7 @@ public class PromptInfo implements Parcelable {
private boolean mReceiveSystemEvents;
@NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
private boolean mAllowBackgroundAuthentication;
+ private boolean mIgnoreEnrollmentState;
public PromptInfo() {
@@ -66,6 +67,7 @@ public class PromptInfo implements Parcelable {
mReceiveSystemEvents = in.readBoolean();
mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
mAllowBackgroundAuthentication = in.readBoolean();
+ mIgnoreEnrollmentState = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -102,6 +104,7 @@ public class PromptInfo implements Parcelable {
dest.writeBoolean(mReceiveSystemEvents);
dest.writeList(mAllowedSensorIds);
dest.writeBoolean(mAllowBackgroundAuthentication);
+ dest.writeBoolean(mIgnoreEnrollmentState);
}
public boolean containsTestConfigurations() {
@@ -192,6 +195,10 @@ public class PromptInfo implements Parcelable {
mAllowBackgroundAuthentication = allow;
}
+ public void setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+ mIgnoreEnrollmentState = ignoreEnrollmentState;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -261,4 +268,8 @@ public class PromptInfo implements Parcelable {
public boolean isAllowBackgroundAuthentication() {
return mAllowBackgroundAuthentication;
}
+
+ public boolean isIgnoreEnrollmentState() {
+ return mIgnoreEnrollmentState;
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a3d595c23095..fe04e5d35784 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -531,7 +531,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, callback, handler, mContext.getUserId());
+ authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, mContext.getUserId(), flags);
}
/**
@@ -541,7 +541,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
@NonNull AuthenticationCallback callback, Handler handler, int userId) {
- authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId);
+ authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId, 0 /* flags */);
}
/**
@@ -550,7 +550,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+ @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId,
+ int flags) {
FrameworkStatsLog.write(FrameworkStatsLog.AUTH_DEPRECATED_API_USED,
AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE,
@@ -566,6 +567,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
return;
}
+ final boolean ignoreEnrollmentState = flags == 0 ? false : true;
+
if (mService != null) {
try {
useHandler(handler);
@@ -573,7 +576,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
final long authId = mService.authenticate(mToken, operationId, sensorId, userId,
- mServiceReceiver, mContext.getOpPackageName());
+ mServiceReceiver, mContext.getOpPackageName(), ignoreEnrollmentState);
if (cancel != null) {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index de94b2fbb5b5..ba1dc6da62a6 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -52,7 +52,8 @@ interface IFingerprintService {
// permission. This is effectively deprecated, since it only comes through FingerprintManager
// now. A requestId is returned that can be used to cancel this operation.
long authenticate(IBinder token, long operationId, int sensorId, int userId,
- IFingerprintServiceReceiver receiver, String opPackageName);
+ IFingerprintServiceReceiver receiver, String opPackageName,
+ boolean shouldIgnoreEnrollmentState);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
// about accept/reject/lockout. A requestId is returned that can be used to cancel this
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index ae97fe7df729..ed617afab96e 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -31,6 +31,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.CompletableFutureUtil;
@@ -272,6 +273,17 @@ final class RemoteInputConnection implements InputConnection {
}
@AnyThread
+ public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ final boolean handled =
+ mInvoker.commitText(text, newCursorPosition, textAttribute);
+ if (handled) {
+ notifyUserActionIfNecessary();
+ }
+ return handled;
+ }
+
+ @AnyThread
private void notifyUserActionIfNecessary() {
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
if (imsInternal == null) {
@@ -311,6 +323,11 @@ final class RemoteInputConnection implements InputConnection {
}
@AnyThread
+ public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ return mInvoker.setComposingRegion(start, end, textAttribute);
+ }
+
+ @AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition) {
final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
if (handled) {
@@ -320,6 +337,16 @@ final class RemoteInputConnection implements InputConnection {
}
@AnyThread
+ public boolean setComposingText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ final boolean handled = mInvoker.setComposingText(text, newCursorPosition, textAttribute);
+ if (handled) {
+ notifyUserActionIfNecessary();
+ }
+ return handled;
+ }
+
+ @AnyThread
public boolean finishComposingText() {
return mInvoker.finishComposingText();
}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7ef5bac092f6..86052484eaf6 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -232,11 +232,10 @@ public final class IpSecAlgorithm implements Parcelable {
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
- // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
- ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
}
private static final Set<String> ENABLED_ALGOS =
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d1e671691897..09e5a8f7382c 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -62,6 +62,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Supplier;
/**
@@ -178,8 +180,12 @@ import java.util.function.Supplier;
* {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
* {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
* {@link #createBinderArray()},
+ * {@link #writeInterfaceArray(T[])}, {@link #readInterfaceArray(T[], Function)},
+ * {@link #createInterfaceArray(IntFunction, Function)},
* {@link #writeBinderList(List)}, {@link #readBinderList(List)},
- * {@link #createBinderArrayList()}.</p>
+ * {@link #createBinderArrayList()},
+ * {@link #writeInterfaceList(List)}, {@link #readInterfaceList(List, Function)},
+ * {@link #createInterfaceArrayList(Function)}.</p>
*
* <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
* can be written and {@link ParcelFileDescriptor} objects returned to operate
@@ -1730,6 +1736,30 @@ public final class Parcel {
}
/**
+ * Flatten a homogeneous array containing an IInterface type into the parcel,
+ * at the current dataPosition() and growing dataCapacity() if needed. The
+ * type of the objects in the array must be one that implements IInterface.
+ *
+ * @param val The array of objects to be written.
+ *
+ * @see #createInterfaceArray
+ * @see #readInterfaceArray
+ * @see IInterface
+ */
+ public final <T extends IInterface> void writeInterfaceArray(
+ @SuppressLint("ArrayReturn") @Nullable T[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeStrongInterface(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
* @hide
*/
public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
@@ -1785,6 +1815,50 @@ public final class Parcel {
}
/**
+ * Read and return a new array of T (IInterface) from the parcel.
+ *
+ * @return the IInterface array of type T
+ * @param newArray a function to create an array of T with a given length
+ * @param asInterface a function to convert IBinder object into T (IInterface)
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection", "SamShouldBeLast"})
+ @Nullable
+ public final <T extends IInterface> T[] createInterfaceArray(
+ @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
+ int N = readInt();
+ if (N >= 0) {
+ T[] val = newArray.apply(N);
+ for (int i=0; i<N; i++) {
+ val[i] = asInterface.apply(readStrongBinder());
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Read an array of T (IInterface) from a parcel.
+ *
+ * @param asInterface a function to convert IBinder object into T (IInterface)
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the length of `val`
+ * mismatches the number of items in the parcel.
+ */
+ public final <T extends IInterface> void readInterfaceArray(
+ @SuppressLint("ArrayReturn") @NonNull T[] val,
+ @NonNull Function<IBinder, T> asInterface) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = asInterface.apply(readStrongBinder());
+ }
+ } else {
+ throw new BadParcelableException("bad array lengths");
+ }
+ }
+
+ /**
* Flatten a List containing a particular object type into the parcel, at
* the current dataPosition() and growing dataCapacity() if needed. The
* type of the objects in the list must be one that implements Parcelable.
@@ -1898,6 +1972,28 @@ public final class Parcel {
}
/**
+ * Flatten a {@code List} containing T (IInterface) objects into this parcel
+ * at the current position. They can later be retrieved with
+ * {@link #createInterfaceArrayList} or {@link #readInterfaceList}.
+ *
+ * @see #createInterfaceArrayList
+ * @see #readInterfaceList
+ */
+ public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ writeStrongInterface(val.get(i));
+ i++;
+ }
+ }
+
+ /**
* Flatten a {@code List} containing arbitrary {@code Parcelable} objects into this parcel
* at the current position. They can later be retrieved using
* {@link #readParcelableList(List, ClassLoader)} if required.
@@ -2896,8 +2992,24 @@ public final class Parcel {
* from the parcel at the current dataPosition().
*/
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
- int N = readInt();
- readMapInternal(outVal, N, loader);
+ int n = readInt();
+ readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
+ }
+
+ /**
+ * Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and
+ * {@code clazzValue} parameter as the types required for each key and value pair.
+ *
+ * @throws BadParcelableException If the item to be deserialized is not an instance of that
+ * class or any of its children class
+ */
+ public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
+ @Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
+ @NonNull Class<V> clazzValue) {
+ Objects.requireNonNull(clazzKey);
+ Objects.requireNonNull(clazzValue);
+ int n = readInt();
+ readMapInternal(outVal, n, loader, clazzKey, clazzValue);
}
/**
@@ -2935,16 +3047,38 @@ public final class Parcel {
@Nullable
public final HashMap readHashMap(@Nullable ClassLoader loader)
{
- int N = readInt();
- if (N < 0) {
+ int n = readInt();
+ if (n < 0) {
return null;
}
- HashMap m = new HashMap(N);
- readMapInternal(m, N, loader);
+ HashMap m = new HashMap(n);
+ readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
return m;
}
/**
+ * Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and
+ * {@code clazzValue} parameter as the types required for each key and value pair.
+ *
+ * @throws BadParcelableException if the item to be deserialized is not an instance of that
+ * class or any of its children class
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
+ @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+ Objects.requireNonNull(clazzKey);
+ Objects.requireNonNull(clazzValue);
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ HashMap<K, V> map = new HashMap<>(n);
+ readMapInternal(map, n, loader, clazzKey, clazzValue);
+ return map;
+ }
+
+ /**
* Read and return a new Bundle object from the parcel at the current
* dataPosition(). Returns null if the previously written Bundle object was
* null.
@@ -3380,6 +3514,32 @@ public final class Parcel {
}
/**
+ * Read and return a new ArrayList containing T (IInterface) objects from
+ * the parcel that was written with {@link #writeInterfaceList} at the
+ * current dataPosition(). Returns null if the
+ * previously written list object was null.
+ *
+ * @return A newly created ArrayList containing T (IInterface)
+ *
+ * @see #writeInterfaceList
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
+ @NonNull Function<IBinder, T> asInterface) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ ArrayList<T> l = new ArrayList<T>(N);
+ while (N > 0) {
+ l.add(asInterface.apply(readStrongBinder()));
+ N--;
+ }
+ return l;
+ }
+
+ /**
* Read into the given List items String objects that were written with
* {@link #writeStringList} at the current dataPosition().
*
@@ -3422,6 +3582,28 @@ public final class Parcel {
}
/**
+ * Read into the given List items IInterface objects that were written with
+ * {@link #writeInterfaceList} at the current dataPosition().
+ *
+ * @see #writeInterfaceList
+ */
+ public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
+ @NonNull Function<IBinder, T> asInterface) {
+ int M = list.size();
+ int N = readInt();
+ int i = 0;
+ for (; i < M && i < N; i++) {
+ list.set(i, asInterface.apply(readStrongBinder()));
+ }
+ for (; i<N; i++) {
+ list.add(asInterface.apply(readStrongBinder()));
+ }
+ for (; i<M; i++) {
+ list.remove(N);
+ }
+ }
+
+ /**
* Read the list of {@code Parcelable} objects at the current data position into the
* given {@code list}. The contents of the {@code list} are replaced. If the serialized
* list was {@code null}, {@code list} is cleared.
@@ -4328,13 +4510,23 @@ public final class Parcel {
destroy();
}
- /* package */ void readMapInternal(@NonNull Map outVal, int N,
+ /**
+ * To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep
+ * the old API for compatibility usages.
+ */
+ /* package */ void readMapInternal(@NonNull Map outVal, int n,
@Nullable ClassLoader loader) {
- while (N > 0) {
- Object key = readValue(loader);
- Object value = readValue(loader);
+ readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
+ }
+
+ /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+ @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+ @Nullable Class<V> clazzValue) {
+ while (n > 0) {
+ K key = readValue(loader, clazzKey);
+ V value = readValue(loader, clazzValue);
outVal.put(key, value);
- N--;
+ n--;
}
}
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
index 74ce5157a548..6c648f136183 100644
--- a/core/java/android/os/health/HealthStats.java
+++ b/core/java/android/os/health/HealthStats.java
@@ -32,7 +32,7 @@ import java.util.Map;
* Each of the keys references data in one of five data types:
*
* <p>
- * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may
+ * A <b>measurement</b> metric contains a single {@code long} value. That value may
* be a count, a time, or some other type of value. The unit for a measurement
* (COUNT, MS, etc) will always be in the name of the constant for the key to
* retrieve it. For example, the
diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java
index afc9d78dcbd1..488a5422becc 100644
--- a/core/java/android/os/health/UidHealthStats.java
+++ b/core/java/android/os/health/UidHealthStats.java
@@ -43,14 +43,14 @@ public final class UidHealthStats {
/**
* How many milliseconds this statistics report covers in wall-clock time while the
- * device was on battery including both screen-on and screen-off time.
+ * device was on battery including only screen-off time.
*/
@HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3;
/**
* How many milliseconds this statistics report covers that the CPU was running while the
- * device was on battery including both screen-on and screen-off time.
+ * device was on battery including only screen-off time.
*/
@HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4;
@@ -65,7 +65,7 @@ public final class UidHealthStats {
/**
* Key for a TimerStat for the times a
- * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK full wake lock}
+ * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock}
* was acquired for this uid.
*/
@HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index dd31e027997f..21c1feba662e 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -648,6 +648,14 @@ public final class DeviceConfig {
public static final String NAMESPACE_GAME_OVERLAY = "game_overlay";
/**
+ * Namespace for Android Virtualization Framework related features accessible by native code.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE =
+ "virtualization_framework_native";
+
+ /**
* Namespace for Constrain Display APIs related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0348107e151d..295ed774f1e4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16609,30 +16609,6 @@ public final class Settings {
public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
/**
- * The bluetooth settings storing duplicate address of companion device.
- * @hide
- */
- public static final String COMPANION_BT_ADDRESS_DUAL = "companion_bt_address_dual";
-
- /**
- * The offset of the visible screen from the display bottom (overscan bottom).
- * @hide
- */
- public static final String BOTTOM_OFFSET = "bottom_offset";
-
- /**
- * The shape of the display.
- * @hide
- */
- public static final String DISPLAY_SHAPE = "display_shape";
-
- // Possible display shapes
- /** @hide */
- public static final int DISPLAY_SHAPE_SQUARE = 0;
- /** @hide */
- public static final int DISPLAY_SHAPE_ROUND = 1;
-
- /**
* The different levels of screen brightness the user can select.
* @hide
*/
@@ -16706,12 +16682,6 @@ public final class Settings {
public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min";
/**
- * The companion device's bluetooth address.
- * @hide
- */
- public static final String COMPANION_ADDRESS = "companion_address";
-
- /**
* What OS does paired device has.
* @hide
*/
@@ -16758,12 +16728,6 @@ public final class Settings {
public static final int HFP_CLIENT_DISABLED = 2;
/**
- * The current HFP client profile setting.
- * @hide
- */
- public static final String HFP_CLIENT_PROFILE_ENABLED = "hfp_client_profile_enabled";
-
- /**
* The companion phone's android version.
* @hide
*/
diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java
index 5370645d31bc..066709fd8744 100644
--- a/core/java/android/util/TimingsTraceLog.java
+++ b/core/java/android/util/TimingsTraceLog.java
@@ -61,13 +61,33 @@ public class TimingsTraceLog {
mTraceTag = traceTag;
mThreadId = Thread.currentThread().getId();
mMaxNestedCalls = maxNestedCalls;
- if (maxNestedCalls > 0) {
- mStartNames = new String[maxNestedCalls];
- mStartTimes = new long[maxNestedCalls];
- } else {
- mStartNames = null;
- mStartTimes = null;
- }
+ this.mStartNames = createAndGetStartNamesArray();
+ this.mStartTimes = createAndGetStartTimesArray();
+ }
+
+ /**
+ * Note: all fields will be copied except for {@code mStartNames} and {@code mStartTimes}
+ * in order to save memory. The copied object is only expected to be used at levels deeper than
+ * the value of {@code mCurrentLevel} when the object is copied.
+ *
+ * @param other object to be copied
+ */
+ protected TimingsTraceLog(TimingsTraceLog other) {
+ this.mTag = other.mTag;
+ this.mTraceTag = other.mTraceTag;
+ this.mThreadId = Thread.currentThread().getId();
+ this.mMaxNestedCalls = other.mMaxNestedCalls;
+ this.mStartNames = createAndGetStartNamesArray();
+ this.mStartTimes = createAndGetStartTimesArray();
+ this.mCurrentLevel = other.mCurrentLevel;
+ }
+
+ private String[] createAndGetStartNamesArray() {
+ return mMaxNestedCalls > 0 ? new String[mMaxNestedCalls] : null;
+ }
+
+ private long[] createAndGetStartTimesArray() {
+ return mMaxNestedCalls > 0 ? new long[mMaxNestedCalls] : null;
}
/**
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index aca17e448b82..c7fd38092ec7 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -63,5 +63,5 @@ oneway interface IRecentsAnimationRunner {
* Called when the task of an activity that has been started while the recents animation
* was running becomes ready for control.
*/
- void onTaskAppeared(in RemoteAnimationTarget app) = 3;
+ void onTasksAppeared(in RemoteAnimationTarget[] app) = 3;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index be1596511432..ce96ecaf9c2f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -217,6 +217,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -729,7 +730,7 @@ public final class ViewRootImpl implements ViewParent,
/**
* This is only used on the RenderThread when handling a blast sync. Specifically, it's only
- * used when calling {@link BLASTBufferQueue#setNextTransaction(Transaction)} and then merged
+ * used when calling {@link BLASTBufferQueue#setSyncTransaction(Transaction)} and then merged
* with a tmp transaction on the Render Thread. The tmp transaction is then merged into
* {@link #mSurfaceChangedTransaction} on the UI Thread, avoiding any threading issues.
*/
@@ -742,6 +743,8 @@ public final class ViewRootImpl implements ViewParent,
*/
private long mRtLastAttemptedDrawFrameNum = 0;
+ private Consumer<SurfaceControl.Transaction> mBLASTDrawConsumer;
+
private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
@@ -3259,6 +3262,9 @@ public final class ViewRootImpl implements ViewParent,
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
+ if (mBLASTDrawConsumer != null) {
+ useBlastSync = true;
+ }
if (!cancelDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -3977,6 +3983,9 @@ public final class ViewRootImpl implements ViewParent,
Log.d(mTag, "Creating frameCompleteCallback");
}
+ final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
+ mBLASTDrawConsumer = null;
+
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
if (DEBUG_BLAST) {
@@ -3990,7 +3999,7 @@ public final class ViewRootImpl implements ViewParent,
// draw attempt. The next transaction and transaction complete callback were only set
// for the current draw attempt.
if (frameWasNotDrawn) {
- mBlastBufferQueue.setNextTransaction(null);
+ mBlastBufferQueue.setSyncTransaction(null);
// Apply the transactions that were sent to mergeWithNextTransaction since the
// frame didn't draw on this vsync. It's possible the frame will draw later, but
// it's better to not be sync than to block on a frame that may never come.
@@ -4002,6 +4011,9 @@ public final class ViewRootImpl implements ViewParent,
mHandler.postAtFrontOfQueue(() -> {
if (useBlastSync) {
mSurfaceChangedTransaction.merge(tmpTransaction);
+ if (blastSyncConsumer != null) {
+ blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ }
}
if (reportNextDraw) {
@@ -4083,7 +4095,7 @@ public final class ViewRootImpl implements ViewParent,
// We don't need to synchronize mRtBLASTSyncTransaction here since it's not
// being modified and only sent to BlastBufferQueue.
- mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+ mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
}
};
registerRtFrameCallback(frameDrawingCallback);
@@ -10457,4 +10469,35 @@ public final class ViewRootImpl implements ViewParent,
listener.onBufferTransformHintChanged(hint);
}
}
+
+ /**
+ * Redirect the next draw of this ViewRoot (from the UI thread perspective)
+ * to the passed in consumer. This can be used to create P2P synchronization
+ * between ViewRoot's however it comes with many caveats.
+ *
+ * 1. You MUST consume the transaction, by either applying it immediately or
+ * merging it in to another transaction. The threading model doesn't
+ * allow you to hold in the passed transaction.
+ * 2. If you merge it in to another transaction, this ViewRootImpl will be
+ * paused until you finally apply that transaction and it receives
+ * the callback from SF. If you lose track of the transaction you will
+ * ANR the app.
+ * 3. Only one person can consume the transaction at a time, if you already
+ * have a pending consumer for this frame, the function will return false
+ * 4. Someone else may have requested to consume the next frame, in which case
+ * this function will return false and you will not receive a callback.
+ * 5. This function does not trigger drawing so even if it returns true you
+ * may not receive a callback unless there is some other UI thread work
+ * to trigger drawing. If it returns true, and a draw occurs, the callback
+ * will be called (Though again watch out for the null transaction case!)
+ * 6. This function must be called on the UI thread. The consumer will likewise
+ * be called on the UI thread.
+ */
+ public boolean consumeNextDraw(Consumer<SurfaceControl.Transaction> consume) {
+ if (mBLASTDrawConsumer != null) {
+ return false;
+ }
+ mBLASTDrawConsumer = consume;
+ return true;
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d76f7894a9de..8287de22c49a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2442,6 +2442,20 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
/**
+ * Flag to indicate that this window will be excluded while computing the magnifiable region
+ * on the un-scaled screen coordinate, which could avoid the cutout on the magnification
+ * border. It should be used for unmagnifiable overlays.
+ *
+ * </p><p>
+ * Note unlike {@link #PRIVATE_FLAG_NOT_MAGNIFIABLE}, this flag doesn't affect the ability
+ * of magnification. If you want to the window to be unmagnifiable and doesn't lead to the
+ * cutout, you need to combine both of them.
+ * </p><p>
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 0x00200000;
+
+ /**
* Flag to prevent the window from being magnified by the accessibility magnifier.
*
* TODO(b/190623172): This is a temporary solution and need to find out another way instead.
@@ -2552,6 +2566,7 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
PRIVATE_FLAG_NOT_MAGNIFIABLE,
PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
@@ -2633,6 +2648,10 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
name = "IS_ROUNDED_CORNERS_OVERLAY"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
+ equals = PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
+ name = "EXCLUDE_FROM_SCREEN_MAGNIFICATION"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_NOT_MAGNIFIABLE,
equals = PRIVATE_FLAG_NOT_MAGNIFIABLE,
name = "NOT_MAGNIFIABLE"),
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index c3d7836a4786..3b15db2ded70 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -545,6 +545,33 @@ public interface InputConnection {
boolean setComposingText(CharSequence text, int newCursorPosition);
/**
+ * The variant of {@link #setComposingText(CharSequence, int)}. This method is
+ * used to allow the IME to provide extra information while setting up composing text.
+ *
+ * @param text The composing text with styles if necessary. If no style
+ * object attached to the text, the default style for composing text
+ * is used. See {@link android.text.Spanned} for how to attach style
+ * object to the text. {@link android.text.SpannableString} and
+ * {@link android.text.SpannableStringBuilder} are two
+ * implementations of the interface {@link android.text.Spanned}.
+ * @param newCursorPosition The new cursor position around the text. If
+ * > 0, this is relative to the end of the text - 1; if <= 0, this
+ * is relative to the start of the text. So a value of 1 will
+ * always advance you to the position after the full text being
+ * inserted. Note that this means you can't position the cursor
+ * within the text, because the editor can make modifications to
+ * the text you are providing so it is not possible to correctly
+ * specify locations there.
+ * @param textAttribute The extra information about the text.
+ * @return true on success, false if the input connection is no longer
+ *
+ */
+ default boolean setComposingText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return setComposingText(text, newCursorPosition);
+ }
+
+ /**
* Mark a certain region of text as composing text. If there was a
* composing region, the characters are left as they were and the
* composing span removed, as if {@link #finishComposingText()}
@@ -579,6 +606,22 @@ public interface InputConnection {
boolean setComposingRegion(int start, int end);
/**
+ * The variant of {@link InputConnection#setComposingRegion(int, int)}. This method is
+ * used to allow the IME to provide extra information while setting up text.
+ *
+ * @param start the position in the text at which the composing region begins
+ * @param end the position in the text at which the composing region ends
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} on success, {@code false} if the input connection is no longer valid.
+ * Since Android {@link android.os.Build.VERSION_CODES#N} until
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
+ * the target application does not implement this method.
+ */
+ default boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ return setComposingRegion(start, end);
+ }
+
+ /**
* Have the text editor finish whatever composing text is
* currently active. This simply leaves the text as-is, removing
* any special composing styling or other state that was around
@@ -634,6 +677,28 @@ public interface InputConnection {
boolean commitText(CharSequence text, int newCursorPosition);
/**
+ * The variant of {@link InputConnection#commitText(CharSequence, int)}. This method is
+ * used to allow the IME to provide extra information while setting up text.
+ *
+ * @param text The text to commit. This may include styles.
+ * @param newCursorPosition The new cursor position around the text,
+ * in Java characters. If > 0, this is relative to the end
+ * of the text - 1; if <= 0, this is relative to the start
+ * of the text. So a value of 1 will always advance the cursor
+ * to the position after the full text being inserted. Note that
+ * this means you can't position the cursor within the text,
+ * because the editor can make modifications to the text
+ * you are providing so it is not possible to correctly specify
+ * locations there.
+ * @param textAttribute The extra information about the text.
+ * @return true on success, false if the input connection is no longer
+ */
+ default boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return commitText(text, newCursorPosition);
+ }
+
+ /**
* Commit a completion the user has selected from the possible ones
* previously reported to {@link InputMethodSession#displayCompletions
* InputMethodSession#displayCompletions(CompletionInfo[])} or
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index a99e9b8aab07..7a88a75f93ad 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
@@ -158,6 +159,16 @@ public class InputConnectionWrapper implements InputConnection {
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean setComposingText(@NonNull CharSequence text,
+ int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ return mTarget.setComposingText(text, newCursorPosition, textAttribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean setComposingRegion(int start, int end) {
return mTarget.setComposingRegion(start, end);
}
@@ -167,6 +178,15 @@ public class InputConnectionWrapper implements InputConnection {
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ return mTarget.setComposingRegion(start, end, textAttribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean finishComposingText() {
return mTarget.finishComposingText();
}
@@ -185,6 +205,16 @@ public class InputConnectionWrapper implements InputConnection {
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return mTarget.commitText(text, newCursorPosition, textAttribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean commitCompletion(CompletionInfo text) {
return mTarget.commitCompletion(text);
}
diff --git a/core/java/android/view/inputmethod/TextAttribute.aidl b/core/java/android/view/inputmethod/TextAttribute.aidl
new file mode 100644
index 000000000000..5f296d9cac74
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextAttribute.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable TextAttribute; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/TextAttribute.java b/core/java/android/view/inputmethod/TextAttribute.java
new file mode 100644
index 000000000000..bc76e780a9e1
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextAttribute.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The data class that IME can take extra information to applications when setting the text.
+ *
+ * See {@link InputConnection#commitText(CharSequence, int, TextAttribute)} and
+ * {@link InputConnection#setComposingRegion(int, int, TextAttribute)} and
+ * {@link InputConnection#setComposingText(CharSequence, int, TextAttribute)}
+ */
+public final class TextAttribute implements Parcelable {
+ private final @NonNull List<String> mTextConversionSuggestions;
+ private final @NonNull PersistableBundle mExtras;
+
+ private TextAttribute(TextAttributeBuilder builder) {
+ mTextConversionSuggestions = builder.mTextConversionSuggestions;
+ mExtras = builder.mExtras;
+ }
+
+ private TextAttribute(Parcel source) {
+ mTextConversionSuggestions = source.createStringArrayList();
+ mExtras = source.readPersistableBundle();
+ }
+
+ /**
+ * Get the list of text conversion suggestions. More text conversion details in
+ * {@link TextAttributeBuilder#setTextConversionSuggestions(List)}.
+ *
+ * @return List of text conversion suggestions. If the list is empty, it means that IME not set
+ * this field or IME didn't have suggestions for applications.
+ */
+ public @NonNull List<String> getTextConversionSuggestions() {
+ return mTextConversionSuggestions;
+ }
+
+ /**
+ * Get the extras data. More extras data details in
+ * {@link TextAttributeBuilder#setExtras(PersistableBundle)}.
+ *
+ * @return Extras data. If the Bundle is empty, it means that IME not set this field or IME
+ * didn't have extras data.
+ */
+ public @NonNull PersistableBundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for creating a {@link TextAttribute}.
+ */
+ public static final class TextAttributeBuilder {
+ private List<String> mTextConversionSuggestions = new ArrayList<>();
+ private PersistableBundle mExtras = new PersistableBundle();
+
+ /**
+ * Sets text conversion suggestions.
+ *
+ * <p>Text conversion suggestion is for some transliteration languages which has
+ * pronunciation characters and target characters. When the user is typing the pronunciation
+ * characters, the input method can insert possible target characters into this list so that
+ * the editor authors can provide suggestion before the user enters the complete
+ * pronunciation characters.</p>
+ *
+ * @param textConversionSuggestions The list of text conversion suggestions.
+ * @return This builder
+ */
+ public @NonNull TextAttributeBuilder setTextConversionSuggestions(
+ @NonNull List<String> textConversionSuggestions) {
+ mTextConversionSuggestions = Collections.unmodifiableList(textConversionSuggestions);
+ return this;
+ }
+
+ /**
+ * Sets extras data.
+ *
+ * <p>Any extra data to supply to the applications. This field is for extended communication
+ * with IME if there is data not defined in framework.</p>
+ *
+ * @return This builder.
+ */
+ public @NonNull TextAttributeBuilder setExtras(@NonNull PersistableBundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * @return a new {@link TextAttribute}.
+ */
+ public @NonNull TextAttribute build() {
+ return new TextAttribute(this);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStringList(mTextConversionSuggestions);
+ dest.writePersistableBundle(mExtras);
+ }
+
+ public static final @NonNull Parcelable.Creator<TextAttribute> CREATOR =
+ new Parcelable.Creator<TextAttribute>() {
+ @Override
+ public TextAttribute createFromParcel(Parcel source) {
+ return new TextAttribute(source);
+ }
+
+ @Override
+ public TextAttribute[] newArray(int size) {
+ return new TextAttribute[size];
+ }
+ };
+}
diff --git a/core/java/android/view/inputmethod/TextSnapshot.java b/core/java/android/view/inputmethod/TextSnapshot.java
index 33ce282780e5..977e6f49e301 100644
--- a/core/java/android/view/inputmethod/TextSnapshot.java
+++ b/core/java/android/view/inputmethod/TextSnapshot.java
@@ -129,8 +129,8 @@ public final class TextSnapshot {
* <p>Values may be any combination of the following values:</p>
* <ul>
* <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_CHARACTERS}</li>
- * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_WORDS}</li>
- * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_SENTENCES}</li>
+ * <li>{@link android.text.TextUtils#CAP_MODE_WORDS TextUtils.CAP_MODE_WORDS}</li>
+ * <li>{@link android.text.TextUtils#CAP_MODE_SENTENCES TextUtils.CAP_MODE_SENTENCES}</li>
* </ul>
*
* <p>You should generally just take a non-zero value to mean "start out in caps mode" though.
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index 165dcdf3a836..a118f9a8188f 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -213,6 +213,7 @@ public final class TaskFragmentInfo implements Parcelable {
+ " isEmpty=" + mIsEmpty
+ " runningActivityCount=" + mRunningActivityCount
+ " isVisible=" + mIsVisible
+ + " activities=" + mActivities
+ " positionInParent=" + mPositionInParent
+ " isTaskClearedForReuse=" + mIsTaskClearedForReuse
+ "}";
diff --git a/core/java/com/android/internal/compat/OWNERS b/core/java/com/android/internal/compat/OWNERS
index cfd0a4b079ad..ee3086ab2fdb 100644
--- a/core/java/com/android/internal/compat/OWNERS
+++ b/core/java/com/android/internal/compat/OWNERS
@@ -1,6 +1 @@
-# Use this reviewer by default.
-platform-compat-eng+reviews@google.com
-
-andreionea@google.com
-mathewi@google.com
-satayev@google.com
+include tools/platform-compat:/OWNERS
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index efdf483563ec..4dbd941b3ae0 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -18,6 +18,7 @@ package com.android.internal.inputmethod;
import android.annotation.AnyThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.KeyEvent;
@@ -27,6 +28,7 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.view.IInputContext;
@@ -211,6 +213,28 @@ public final class IInputContextInvoker {
}
/**
+ * Invokes {@link IInputContext#commitTextWithTextAttribute(InputConnectionCommandHeader, int,
+ * CharSequence)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mIInputContext.commitTextWithTextAttribute(
+ createHeader(), text, newCursorPosition, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IInputContext#commitCompletion(InputConnectionCommandHeader, CompletionInfo)}.
*
* @param text {@code text} parameter to be passed.
@@ -315,6 +339,27 @@ public final class IInputContextInvoker {
}
/**
+ * Invokes {@link IInputContext#setComposingRegionWithTextAttribute(
+ * InputConnectionCommandHeader, int, int, TextAttribute)}.
+ *
+ * @param start {@code id} parameter to be passed.
+ * @param end {@code id} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ try {
+ mIInputContext.setComposingRegionWithTextAttribute(
+ createHeader(), start, end, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes
* {@link IInputContext#setComposingText(InputConnectionCommandHeader, CharSequence, int)}.
*
@@ -334,6 +379,28 @@ public final class IInputContextInvoker {
}
/**
+ * Invokes {@link IInputContext#setComposingTextWithTextAttribute(InputConnectionCommandHeader,
+ * CharSequence, int, TextAttribute)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mIInputContext.setComposingTextWithTextAttribute(
+ createHeader(), text, newCursorPosition, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IInputContext#finishComposingText(InputConnectionCommandHeader)}.
*
* @return {@code true} if the invocation is completed without {@link RemoteException}.
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 21358abe243f..550322627794 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -42,6 +42,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
@@ -385,6 +386,23 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
@Dispatching(cancellable = true)
@Override
+ public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("commitTextWithTextAttribute", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitText on inactive InputConnection");
+ return;
+ }
+ ic.commitText(text, newCursorPosition, textAttribute);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
dispatchWithTracing("commitCompletion", () -> {
if (header.mSessionId != mCurrentSessionId.get()) {
@@ -489,6 +507,23 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
@Dispatching(cancellable = true)
@Override
+ public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
+ int end, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setComposingRegion on inactive InputConnection");
+ return;
+ }
+ ic.setComposingRegion(start, end, textAttribute);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("setComposingText", () -> {
@@ -504,6 +539,23 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
});
}
+ @Dispatching(cancellable = true)
+ @Override
+ public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
+ CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setComposingText on inactive InputConnection");
+ return;
+ }
+ ic.setComposingText(text, newCursorPosition, textAttribute);
+ });
+ }
+
/**
* Dispatches {@link InputConnection#finishComposingText()}.
*
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index ac83987ef12c..dd4ff672c061 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -30,7 +30,9 @@ import java.lang.annotation.Retention;
@IntDef(flag = true, value = {
StartInputFlags.VIEW_HAS_FOCUS,
StartInputFlags.IS_TEXT_EDITOR,
- StartInputFlags.INITIAL_CONNECTION})
+ StartInputFlags.INITIAL_CONNECTION,
+ StartInputFlags.WINDOW_GAINED_FOCUS,
+})
public @interface StartInputFlags {
/**
* There is a focused view in the focused window.
@@ -40,17 +42,17 @@ public @interface StartInputFlags {
/**
* The focused view is a text editor.
*/
- int IS_TEXT_EDITOR = 2;
+ int IS_TEXT_EDITOR = 1 << 1;
/**
* An internal concept to distinguish "start" and "restart". This concept doesn't look well
* documented hence we probably need to revisit this though.
*/
- int INITIAL_CONNECTION = 4;
+ int INITIAL_CONNECTION = 1 << 2;
/**
* The start input happens when the window gained focus to call
* {@code android.view.inputmethod.InputMethodManager#startInputAsyncOnWindowFocusGain}.
*/
- int WINDOW_GAINED_FOCUS = 8;
+ int WINDOW_GAINED_FOCUS = 1 << 3;
}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index df55bebb35ef..7da0f116c358 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -22,6 +22,7 @@ import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.InputConnectionCommandHeader;
@@ -52,10 +53,16 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void setComposingText(in InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition);
+ void setComposingTextWithTextAttribute(in InputConnectionCommandHeader header,
+ CharSequence text, int newCursorPosition, in TextAttribute textAttribute);
+
void finishComposingText(in InputConnectionCommandHeader header);
void commitText(in InputConnectionCommandHeader header, CharSequence text,
- int newCursorPosition);
+ int newCursorPosition);
+
+ void commitTextWithTextAttribute(in InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, in TextAttribute textAttribute);
void commitCompletion(in InputConnectionCommandHeader header, in CompletionInfo completion);
@@ -82,6 +89,9 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
+ void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
+ int end, in TextAttribute textAttribute);
+
void getSelectedText(in InputConnectionCommandHeader header, int flags,
in AndroidFuture future /* T=CharSequence */);
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a7362ab3c3fa..cdfd08971905 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -61,10 +61,10 @@ static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
queue->getSurface(includeSurfaceControlHandle));
}
-static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
+static void nativeSetSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
- queue->setNextTransaction(transaction);
+ queue->setSyncTransaction(transaction);
}
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
@@ -98,7 +98,7 @@ static const JNINativeMethod gMethods[] = {
{"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
- {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
+ {"nativeSetSyncTransaction", "(JJ)V", (void*)nativeSetSyncTransaction},
{"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index be82879c8411..ef6fd7dd6829 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -36,6 +36,7 @@
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <linux/fs.h>
#include <memory>
@@ -253,6 +254,16 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr
return INSTALL_FAILED_CONTAINER_ERROR;
}
+ // If a filesystem like f2fs supports per-file compression, set the compression bit before data
+ // writes
+ unsigned int flags;
+ if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
+ ALOGE("Failed to call FS_IOC_GETFLAGS on %s: %s\n", localTmpFileName, strerror(errno));
+ } else if ((flags & FS_COMPR_FL) == 0) {
+ flags |= FS_COMPR_FL;
+ ioctl(fd, FS_IOC_SETFLAGS, &flags);
+ }
+
if (!zipFile->uncompressEntry(zipEntry, fd)) {
ALOGE("Failed uncompressing %s to %s\n", fileName, localTmpFileName);
close(fd);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 3248cf513c84..8ef38254a38b 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -262,6 +262,7 @@ message DisplayRotationProto {
optional int32 user_rotation = 3 [(.android.typedef) = "android.view.Surface.Rotation"];
optional int32 fixed_to_user_rotation_mode = 4;
optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+ optional bool is_fixed_to_user_rotation = 6;
}
/* represents DockedTaskDividerController */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 240d3aa88c52..6d96784d0e0a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1900,7 +1900,7 @@
@hide This should only be used by ManagedProvisioning app.
-->
<permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows Carrier Provisioning to call methods in Networking services
<p>Not for use by any other third-party or privileged applications.
@@ -2356,10 +2356,10 @@
<permission android:name="android.permission.OEM_UNLOCK_STATE"
android:protectionLevel="signature" />
- <!-- @hide Allows querying state of PersistentDataBlock
+ <!-- @SystemApi @hide Allows querying state of PersistentDataBlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_PDB_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows testing if a passwords is forbidden by the admins.
@hide <p>Not for use by third-party applications. -->
@@ -2417,7 +2417,7 @@
<!-- @SystemApi @TestApi Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows to read device identifiers and use ICC based authentication like EAP-AKA.
Often required in authentication to access the carrier's server and manage services
@@ -2733,25 +2733,25 @@
user-targeted broadcasts. This permission is not available to
third party applications. -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
types of interactions
@hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|role" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<!-- Allows interaction across profiles in the same profile group. -->
<permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
android:protectionLevel="signature|appop" />
- <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can
- interact across profiles in the same profile group.
+ <!-- @SystemApi Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that
+ they can interact across profiles in the same profile group.
@hide -->
<permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
@@ -2772,10 +2772,10 @@
<permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
android:protectionLevel="signature|privileged|development|role" />
- <!-- @hide Allows an application to set the profile owners and the device owner.
+ <!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature"
+ android:protectionLevel="signature|role"
android:label="@string/permlab_manageProfileAndDeviceOwners"
android:description="@string/permdesc_manageProfileAndDeviceOwners" />
@@ -2828,7 +2828,7 @@
<!-- @SystemApi @hide Allows an application to start activities from background -->
<permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
- android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
+ android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role" />
<!-- Allows an application to start foreground services from the background at any time.
<em>This permission is not for use by third-party applications</em>,
@@ -2990,6 +2990,17 @@
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
android:protectionLevel="signature|privileged" />
+ <!-- Allows application to request to be associated with a vehicle head unit capable of
+ automotive projection
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
+ android:protectionLevel="internal|role" />
+
<!-- Allows a companion app to associate to Wi-Fi.
<p>Only for use by a single pre-approved app.
@hide
@@ -3054,7 +3065,7 @@
<!-- Allows applications to set the system time directly.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows applications to set the system time zone directly.
<p>Not for use by third-party applications.
@@ -3062,7 +3073,7 @@
<permission android:name="android.permission.SET_TIME_ZONE"
android:label="@string/permlab_setTimeZone"
android:description="@string/permdesc_setTimeZone"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows telephony to suggest the time / time zone.
<p>Not for use by third-party applications.
@@ -3176,7 +3187,7 @@
as locale.
<p>Protection level: signature|privileged|development -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- Allows an application to read or write the system settings.
@@ -3193,7 +3204,7 @@
<permission android:name="android.permission.WRITE_SETTINGS"
android:label="@string/permlab_writeSettings"
android:description="@string/permdesc_writeSettings"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:protectionLevel="signature|preinstalled|appop|pre23|role" />
<!-- Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
@@ -3446,7 +3457,7 @@
<!-- Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
@@ -3578,6 +3589,13 @@
<permission android:name="android.permission.GET_APP_OPS_STATS"
android:protectionLevel="signature|privileged|development" />
+ <!-- @SystemApi @hide Allows an application to collect historical application operation
+ statistics.
+ <p>Not for use by third party applications.
+ -->
+ <permission android:name="android.permission.GET_HISTORICAL_APP_OPS_STATS"
+ android:protectionLevel="internal|role" />
+
<!-- @SystemApi Allows an application to update application operation statistics. Not for
use by third party apps.
@hide -->
@@ -3699,7 +3717,7 @@
to put the higher-level system there into a shutdown state.
@hide -->
<permission android:name="android.permission.SHUTDOWN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to tell the activity manager to temporarily
stop application switches, putting it into a special mode that
@@ -4070,14 +4088,13 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_DEVICE_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi Required to add or remove another application as a device admin.
<p>Not for use by third-party applications.
- @hide
- @removed -->
+ @hide -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi Allows an app to reset the device password.
<p>Not for use by third-party applications.
@@ -4190,14 +4207,14 @@
<permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to install existing system packages. This is a limited
+ <!-- @SystemApi Allows an application to install existing system packages. This is a limited
version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
<p>Not for use by third-party applications.
TODO(b/80204953): remove this permission once we have a long-term solution.
@hide
-->
<permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows an application to use the package installer v2 APIs.
<p>The package installer v2 APIs are still a work in progress and we're
@@ -4292,7 +4309,7 @@
when the application deleting the package is not the same application that installed the
package. -->
<permission android:name="android.permission.DELETE_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to move location of installed package.
@hide -->
@@ -4308,7 +4325,7 @@
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to grant specific permissions.
@hide -->
@@ -4760,7 +4777,7 @@
<!-- Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
@@ -4771,7 +4788,7 @@
<!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
<permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to perform SIM Activation @hide -->
<permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
@@ -4998,7 +5015,7 @@
@hide
-->
<permission android:name="android.permission.CRYPT_KEEPER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to read historical network usage for
specific networks and applications. @hide -->
@@ -5143,10 +5160,10 @@
android:protectionLevel="signature|installer" />
<uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
- <!-- Allows notifications to be colorized
+ <!-- @SystemApi Allows notifications to be colorized
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup|role" />
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
@@ -5387,7 +5404,7 @@
<!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
@hide -->
<permission android:name="android.permission.PEERS_MAC_ADDRESS"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup|role" />
<!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
can use this permission to ensure incoming Nfc messages are from the Nfc stack
@@ -5692,11 +5709,11 @@
<permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
android:protectionLevel="signature" />
- <!-- Allows an app to mark a profile owner as managing an organization-owned device.
+ <!-- @SystemApi Allows an app to mark a profile owner as managing an organization-owned device.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MARK_DEVICE_ORGANIZATION_OWNED"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows financial apps to read filtered sms messages.
Protection level: signature|appop
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 755938eefdbd..7805d46188e9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2953,6 +2953,9 @@
usually TVs.
<p>Requires permission {@code android.permission.DISABLE_SYSTEM_SOUND_EFFECTS}. -->
<attr name="playHomeTransitionSound" format="boolean"/>
+ <!-- Indicates whether the activity can be displayed on a remote device which may or
+ may not be running Android. -->
+ <attr name="canDisplayOnRemoteDevices" format="boolean"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7f68bfd622cc..b924bd231361 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1956,8 +1956,9 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
- <!-- Flag indicating whether the volume panel should show remote sessions. -->
- <bool name="config_volumeShowRemoteSessions">true</bool>
+ <!-- Flag indicating whether platform level volume adjustments are enabled for remote sessions
+ on grouped devices. -->
+ <bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool>
<!-- Flag indicating that an outbound call must have a call capable phone account
that has declared it can process the call's handle. -->
@@ -2050,6 +2051,8 @@
<!-- The name of the package that will hold the television remote service role.
TODO(b/189347385) make this a @SystemAPI -->
<string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
+ <!-- The name of the package that will hold the device management role -->
+ <string name="config_deviceManager" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 366dccb67e72..2820f86c920a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3303,6 +3303,7 @@
<public name="sharedUserMaxSdkVersion" />
<public name="requiredSplitTypes" />
<public name="splitTypes" />
+ <public name="canDisplayOnRemoteDevices" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
@@ -3312,12 +3313,14 @@
<public name="accessibilityActionSwipeDown" />
</staging-public-group>
- <staging-public-group type="style" first-id="0x0dfd0000">
+ <staging-public-group type="style" first-id="0x01dd0000">
</staging-public-group>
<staging-public-group type="string" first-id="0x01dc0000">
<!-- @hide @SystemApi -->
<public name="config_systemSupervision" />
+ <!-- @hide @SystemApi -->
+ <public name="config_deviceManager" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f166b733042f..5208c4a9230d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4557,7 +4557,7 @@
<java-symbol type="dimen" name="config_wallpaperDimAmount" />
- <java-symbol type="bool" name="config_volumeShowRemoteSessions" />
+ <java-symbol type="bool" name="config_volumeAdjustmentForRemoteGroupSessions" />
<!-- List of shared library packages that should be loaded by the classloader after the
code and resources provided by applications. -->
diff --git a/core/tests/PlatformCompatFramework/OWNERS b/core/tests/PlatformCompatFramework/OWNERS
index cfd0a4b079ad..ee3086ab2fdb 100644
--- a/core/tests/PlatformCompatFramework/OWNERS
+++ b/core/tests/PlatformCompatFramework/OWNERS
@@ -1,6 +1 @@
-# Use this reviewer by default.
-platform-compat-eng+reviews@google.com
-
-andreionea@google.com
-mathewi@google.com
-satayev@google.com
+include tools/platform-compat:/OWNERS
diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml
index 01a28a8661cb..bc62bbc845f3 100644
--- a/data/etc/com.android.cellbroadcastreceiver.xml
+++ b/data/etc/com.android.cellbroadcastreceiver.xml
@@ -19,6 +19,7 @@
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 33cc61b8f84f..6983aa4aafc9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -49,6 +49,7 @@ applications that come with the platform
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 9af508a2d2c4..405f66dd5d56 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -31,7 +31,7 @@ public final class BLASTBufferQueue {
long height, int format);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
- private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
+ private static native void nativeSetSyncTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
int format, long transactionPtr);
private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
@@ -70,8 +70,8 @@ public final class BLASTBufferQueue {
* This gives the caller a chance to apply the transaction when it's ready.
* @param t The transaction to add the frame to. This can be null to clear the transaction.
*/
- public void setNextTransaction(@Nullable SurfaceControl.Transaction t) {
- nativeSetNextTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
+ public void setSyncTransaction(@Nullable SurfaceControl.Transaction t) {
+ nativeSetSyncTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 06e7d1457417..44af1a9fd780 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -73,14 +73,61 @@ class SplitContainer {
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule)
- && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary();
+ && ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary()
+ != SplitRule.FINISH_NEVER;
return shouldFinishPrimaryWithSecondary || isPlaceholderContainer;
}
static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) {
final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule)
- && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary();
+ && ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary()
+ != SplitRule.FINISH_NEVER;
return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
}
+
+ static boolean shouldFinishAssociatedContainerWhenStacked(int finishBehavior) {
+ return finishBehavior == SplitRule.FINISH_ALWAYS;
+ }
+
+ static boolean shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior) {
+ return finishBehavior == SplitRule.FINISH_ALWAYS
+ || finishBehavior == SplitRule.FINISH_ADJACENT;
+ }
+
+ static int getFinishPrimaryWithSecondaryBehavior(@NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPlaceholderRule) {
+ return ((SplitPlaceholderRule) splitRule).getFinishPrimaryWithSecondary();
+ }
+ if (splitRule instanceof SplitPairRule) {
+ return ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary();
+ }
+ return SplitRule.FINISH_NEVER;
+ }
+
+ static int getFinishSecondaryWithPrimaryBehavior(@NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPlaceholderRule) {
+ return SplitRule.FINISH_ALWAYS;
+ }
+ if (splitRule instanceof SplitPairRule) {
+ return ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary();
+ }
+ return SplitRule.FINISH_NEVER;
+ }
+
+ static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) {
+ if (!(splitRule instanceof SplitPlaceholderRule)) {
+ return false;
+ }
+ return ((SplitPlaceholderRule) splitRule).isSticky();
+ }
+
+ @Override
+ public String toString() {
+ return "SplitContainer{"
+ + " primaryContainer=" + mPrimaryContainer
+ + " secondaryContainer=" + mSecondaryContainer
+ + " splitRule=" + mSplitRule
+ + "}";
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 20515e71a91b..68c19041940c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -16,6 +16,12 @@
package androidx.window.extensions.embedding;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -460,6 +466,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false;
}
+ if (isStickyPlaceholderRule(splitContainer.getSplitRule())) {
+ // The placeholder should remain after it was first shown.
+ return false;
+ }
+
if (mPresenter.shouldShowSideBySide(splitContainer)) {
return false;
}
@@ -497,7 +508,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return;
}
List<SplitInfo> currentSplitStates = getActiveSplitStates();
- if (mLastReportedSplitStates.equals(currentSplitStates)) {
+ if (currentSplitStates == null || mLastReportedSplitStates.equals(currentSplitStates)) {
return;
}
mLastReportedSplitStates.clear();
@@ -506,15 +517,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/**
- * Returns a list of descriptors for currently active split states.
+ * @return a list of descriptors for currently active split states. If the value returned is
+ * null, that indicates that the active split states are in an intermediate state and should
+ * not be reported.
*/
+ @Nullable
private List<SplitInfo> getActiveSplitStates() {
List<SplitInfo> splitStates = new ArrayList<>();
for (SplitContainer container : mSplitContainers) {
if (container.getPrimaryContainer().isEmpty()
|| container.getSecondaryContainer().isEmpty()) {
- // Skipping containers that do not have any activities to report.
- continue;
+ // We are in an intermediate state because either the split container is about to be
+ // removed or the primary or secondary container are about to receive an activity.
+ return null;
}
ActivityStack primaryContainer = container.getPrimaryContainer().toActivityStack();
ActivityStack secondaryContainer = container.getSecondaryContainer().toActivityStack();
@@ -639,6 +654,52 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false;
}
+ /**
+ * Checks whether the associated container should be destroyed together with a finishing
+ * container. There is a case when primary containers for placeholders should be retained
+ * despite the rule configuration to finish primary with secondary - if they are marked as
+ * 'sticky' and the placeholder was finished when fully overlapping the primary container.
+ * @return {@code true} if the associated container should be retained (and not be finished).
+ */
+ boolean shouldRetainAssociatedContainer(@NonNull TaskFragmentContainer finishingContainer,
+ @NonNull TaskFragmentContainer associatedContainer) {
+ SplitContainer splitContainer = getActiveSplitForContainers(associatedContainer,
+ finishingContainer);
+ if (splitContainer == null) {
+ // Containers are not in the same split, no need to retain.
+ return false;
+ }
+ // Find the finish behavior for the associated container
+ int finishBehavior;
+ SplitRule splitRule = splitContainer.getSplitRule();
+ if (finishingContainer == splitContainer.getPrimaryContainer()) {
+ finishBehavior = getFinishSecondaryWithPrimaryBehavior(splitRule);
+ } else {
+ finishBehavior = getFinishPrimaryWithSecondaryBehavior(splitRule);
+ }
+ // Decide whether the associated container should be retained based on the current
+ // presentation mode.
+ if (mPresenter.shouldShowSideBySide(splitContainer)) {
+ return !shouldFinishAssociatedContainerWhenAdjacent(finishBehavior);
+ } else {
+ return !shouldFinishAssociatedContainerWhenStacked(finishBehavior);
+ }
+ }
+
+ /**
+ * @see #shouldRetainAssociatedContainer(TaskFragmentContainer, TaskFragmentContainer)
+ */
+ boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer,
+ @NonNull Activity associatedActivity) {
+ TaskFragmentContainer associatedContainer = getContainerWithActivity(
+ associatedActivity.getActivityToken());
+ if (associatedContainer == null) {
+ return false;
+ }
+
+ return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
+ }
+
private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 80d9c2c1719c..a1a53bc93781 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -27,6 +27,7 @@ import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -227,6 +228,9 @@ class TaskFragmentContainer {
// Finish dependent containers
for (TaskFragmentContainer container : mContainersToFinishOnExit) {
+ if (controller.shouldRetainAssociatedContainer(this, container)) {
+ continue;
+ }
container.finish(true /* shouldFinishDependent */, presenter,
wct, controller);
}
@@ -234,6 +238,9 @@ class TaskFragmentContainer {
// Finish associated activities
for (Activity activity : mActivitiesToFinishOnExit) {
+ if (controller.shouldRetainAssociatedActivity(this, activity)) {
+ continue;
+ }
activity.finish();
}
mActivitiesToFinishOnExit.clear();
@@ -267,4 +274,42 @@ class TaskFragmentContainer {
mLastRequestedBounds.set(bounds);
}
}
+
+ @Override
+ public String toString() {
+ return toString(true /* includeContainersToFinishOnExit */);
+ }
+
+ /**
+ * @return string for this TaskFragmentContainer and includes containers to finish on exit
+ * based on {@code includeContainersToFinishOnExit}. If containers to finish on exit are always
+ * included in the string, then calling {@link #toString()} on a container that mutually
+ * finishes with another container would cause a stack overflow.
+ */
+ private String toString(boolean includeContainersToFinishOnExit) {
+ return "TaskFragmentContainer{"
+ + " token=" + mToken
+ + " info=" + mInfo
+ + " topNonFinishingActivity=" + getTopNonFinishingActivity()
+ + " pendingAppearedActivities=" + mPendingAppearedActivities
+ + (includeContainersToFinishOnExit ? " containersToFinishOnExit="
+ + containersToFinishOnExitToString() : "")
+ + " activitiesToFinishOnExit=" + mActivitiesToFinishOnExit
+ + " isFinished=" + mIsFinished
+ + " lastRequestedBounds=" + mLastRequestedBounds
+ + "}";
+ }
+
+ private String containersToFinishOnExitToString() {
+ StringBuilder sb = new StringBuilder("[");
+ Iterator<TaskFragmentContainer> containerIterator = mContainersToFinishOnExit.iterator();
+ while (containerIterator.hasNext()) {
+ sb.append(containerIterator.next().toString(
+ false /* includeContainersToFinishOnExit */));
+ if (containerIterator.hasNext()) {
+ sb.append(", ");
+ }
+ }
+ return sb.append("]").toString();
+ }
}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 830d13dd6dc5..d6678bf9b320 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index f7af4e1dd1d1..caa532761f1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1036,10 +1036,9 @@ public class BubbleController {
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble
- && !entry.getRanking().isSuspended()) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
- onEntryUpdated(entry, true /* shouldBubbleUp */);
+ onEntryUpdated(entry, shouldBubbleUp && !entry.getRanking().isSuspended());
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 836a6f610bbd..7cf3bafe499a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -41,10 +41,13 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.util.StagedSplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Manages the recent task list from the system, caching it as necessary.
@@ -62,6 +65,13 @@ public class RecentTasksController implements TaskStackListenerCallback,
// Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
// pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
private final SparseIntArray mSplitTasks = new SparseIntArray();
+ /**
+ * Maps taskId to {@link StagedSplitBounds} for both taskIDs.
+ * Meaning there will be two taskId integers mapping to the same object.
+ * If there's any ordering to the pairing than we can probably just get away with only one
+ * taskID mapping to it, leaving both for consistency with {@link #mSplitTasks} for now.
+ */
+ private final Map<Integer, StagedSplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
@@ -97,15 +107,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
- public void addSplitPair(int taskId1, int taskId2) {
+ public void addSplitPair(int taskId1, int taskId2, StagedSplitBounds splitBounds) {
if (taskId1 == taskId2) {
return;
}
// Remove any previous pairs
removeSplitPair(taskId1);
removeSplitPair(taskId2);
+ mTaskSplitBoundsMap.remove(taskId1);
+ mTaskSplitBoundsMap.remove(taskId2);
+
mSplitTasks.put(taskId1, taskId2);
mSplitTasks.put(taskId2, taskId1);
+ mTaskSplitBoundsMap.put(taskId1, splitBounds);
+ mTaskSplitBoundsMap.put(taskId2, splitBounds);
}
/**
@@ -116,6 +131,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
if (pairedTaskId != INVALID_TASK_ID) {
mSplitTasks.delete(taskId);
mSplitTasks.delete(pairedTaskId);
+ mTaskSplitBoundsMap.remove(taskId);
+ mTaskSplitBoundsMap.remove(pairedTaskId);
}
}
@@ -203,7 +220,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
if (pairedTaskId != INVALID_TASK_ID) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo));
+ recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
+ mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
}
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 8471e1e58109..3589f7c14cd3 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
@@ -96,6 +96,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.StagedSplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -691,11 +692,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
mRecentTasks.ifPresent(recentTasks -> {
+ Rect topLeftBounds = mSplitLayout.getBounds1();
+ Rect bottomRightBounds = mSplitLayout.getBounds2();
int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId();
int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId();
+ boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
+ int leftTopTaskId;
+ int rightBottomTaskId;
+ if (sideStageTopLeft) {
+ leftTopTaskId = sideStageTopTaskId;
+ rightBottomTaskId = mainStageTopTaskId;
+ } else {
+ leftTopTaskId = mainStageTopTaskId;
+ rightBottomTaskId = sideStageTopTaskId;
+ }
+ StagedSplitBounds splitBounds = new StagedSplitBounds(topLeftBounds, bottomRightBounds,
+ leftTopTaskId, rightBottomTaskId);
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
- recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId);
+ recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
}
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index 0331ba19defe..603d05d78fc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -30,25 +30,34 @@ import androidx.annotation.Nullable;
public class GroupedRecentTaskInfo implements Parcelable {
public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
+ public @Nullable StagedSplitBounds mStagedSplitBounds;
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
- this(task1, null);
+ this(task1, null, null);
}
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
- @Nullable ActivityManager.RecentTaskInfo task2) {
+ @Nullable ActivityManager.RecentTaskInfo task2,
+ @Nullable StagedSplitBounds stagedSplitBounds) {
mTaskInfo1 = task1;
mTaskInfo2 = task2;
+ mStagedSplitBounds = stagedSplitBounds;
}
GroupedRecentTaskInfo(Parcel parcel) {
mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ mStagedSplitBounds = parcel.readTypedObject(StagedSplitBounds.CREATOR);
}
@Override
public String toString() {
- return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2);
+ String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
+ + ", Task2: " + getTaskInfo(mTaskInfo2);
+ if (mStagedSplitBounds != null) {
+ taskString += ", SplitBounds: " + mStagedSplitBounds.toString();
+ }
+ return taskString;
}
private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
@@ -67,6 +76,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedObject(mTaskInfo1, flags);
parcel.writeTypedObject(mTaskInfo2, flags);
+ parcel.writeTypedObject(mStagedSplitBounds, flags);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
new file mode 100644
index 000000000000..aadf792c572f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.util;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container of various information needed to display split screen
+ * tasks/leashes/etc in Launcher
+ */
+public class StagedSplitBounds implements Parcelable {
+ public final Rect leftTopBounds;
+ public final Rect rightBottomBounds;
+ /** This rect represents the actual gap between the two apps */
+ public final Rect visualDividerBounds;
+ // This class is orientation-agnostic, so we compute both for later use
+ public final float topTaskPercent;
+ public final float leftTaskPercent;
+ /**
+ * If {@code true}, that means at the time of creation of this object, the
+ * split-screened apps were vertically stacked. This is useful in scenarios like
+ * rotation where the bounds won't change, but this variable can indicate what orientation
+ * the bounds were originally in
+ */
+ public final boolean appsStackedVertically;
+ public final int leftTopTaskId;
+ public final int rightBottomTaskId;
+
+ public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
+ int leftTopTaskId, int rightBottomTaskId) {
+ this.leftTopBounds = leftTopBounds;
+ this.rightBottomBounds = rightBottomBounds;
+ this.leftTopTaskId = leftTopTaskId;
+ this.rightBottomTaskId = rightBottomTaskId;
+
+ if (rightBottomBounds.top > leftTopBounds.top) {
+ // vertical apps, horizontal divider
+ this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
+ leftTopBounds.right, rightBottomBounds.top);
+ appsStackedVertically = true;
+ } else {
+ // horizontal apps, vertical divider
+ this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
+ rightBottomBounds.left, leftTopBounds.bottom);
+ appsStackedVertically = false;
+ }
+
+ leftTaskPercent = this.leftTopBounds.width() / (float) rightBottomBounds.right;
+ topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
+ }
+
+ public StagedSplitBounds(Parcel parcel) {
+ leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
+ rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
+ visualDividerBounds = parcel.readTypedObject(Rect.CREATOR);
+ topTaskPercent = parcel.readFloat();
+ leftTaskPercent = parcel.readFloat();
+ appsStackedVertically = parcel.readBoolean();
+ leftTopTaskId = parcel.readInt();
+ rightBottomTaskId = parcel.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedObject(leftTopBounds, flags);
+ parcel.writeTypedObject(rightBottomBounds, flags);
+ parcel.writeTypedObject(visualDividerBounds, flags);
+ parcel.writeFloat(topTaskPercent);
+ parcel.writeFloat(leftTaskPercent);
+ parcel.writeBoolean(appsStackedVertically);
+ parcel.writeInt(leftTopTaskId);
+ parcel.writeInt(rightBottomTaskId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+ + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId + "\n"
+ + "Divider: " + visualDividerBounds + "\n"
+ + "AppsVertical? " + appsStackedVertically;
+ }
+
+ public static final Creator<StagedSplitBounds> CREATOR = new Creator<StagedSplitBounds>() {
+ @Override
+ public StagedSplitBounds createFromParcel(Parcel in) {
+ return new StagedSplitBounds(in);
+ }
+
+ @Override
+ public StagedSplitBounds[] newArray(int size) {
+ return new StagedSplitBounds[size];
+ }
+ };
+}
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index d80699de8a2d..f49e80ae16b1 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 909476
# includes OWNERS from parent directories
natanieljr@google.com
+pablogamito@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index a1e12319ac70..19a5417aace6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -20,6 +20,8 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -31,10 +33,9 @@ import static org.mockito.Mockito.verify;
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
-import android.app.WindowConfiguration;
import android.content.Context;
+import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.window.TaskAppearedInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -45,6 +46,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.util.StagedSplitBounds;
import org.junit.Before;
import org.junit.Test;
@@ -106,8 +108,11 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- mRecentTasksController.addSplitPair(t2.taskId, t4.taskId);
- mRecentTasksController.addSplitPair(t3.taskId, t5.taskId);
+ StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 4);
+ StagedSplitBounds pair2Bounds = new StagedSplitBounds(new Rect(), new Rect(), 3, 5);
+
+ mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
+ mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -126,7 +131,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3);
// Add a pair
- mRecentTasksController.addSplitPair(t2.taskId, t3.taskId);
+ StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 3);
+ mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
reset(mRecentTasksController);
// Remove one of the tasks and ensure the pair is removed
@@ -201,10 +207,23 @@ public class RecentTasksControllerTest extends ShellTestCase {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
GroupedRecentTaskInfo pair = recentTasks.get(i);
- flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId;
+ int taskId1 = pair.mTaskInfo1.taskId;
+ flattenedTaskIds[2 * i] = taskId1;
flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
? pair.mTaskInfo2.taskId
: -1;
+
+ if (pair.mTaskInfo2 != null) {
+ assertNotNull(pair.mStagedSplitBounds);
+ int leftTopTaskId = pair.mStagedSplitBounds.leftTopTaskId;
+ int bottomRightTaskId = pair.mStagedSplitBounds.rightBottomTaskId;
+ // Unclear if pairs are ordered by split position, most likely not.
+ assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
+ assertTrue(bottomRightTaskId == taskId1
+ || bottomRightTaskId == pair.mTaskInfo2.taskId);
+ } else {
+ assertNull(pair.mStagedSplitBounds);
+ }
}
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
+ " Received: " + Arrays.toString(flattenedTaskIds),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java
new file mode 100644
index 000000000000..ad73c56950bd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java
@@ -0,0 +1,94 @@
+package com.android.wm.shell.recents;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wm.shell.util.StagedSplitBounds;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class StagedSplitBoundsTest {
+ private static final int DEVICE_WIDTH = 100;
+ private static final int DEVICE_LENGTH = 200;
+ private static final int DIVIDER_SIZE = 20;
+ private static final int TASK_ID_1 = 4;
+ private static final int TASK_ID_2 = 9;
+
+ // Bounds in screen space
+ private final Rect mTopRect = new Rect();
+ private final Rect mBottomRect = new Rect();
+ private final Rect mLeftRect = new Rect();
+ private final Rect mRightRect = new Rect();
+
+ @Before
+ public void setup() {
+ mTopRect.set(0, 0, DEVICE_WIDTH, DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2);
+ mBottomRect.set(0, DEVICE_LENGTH / 2 + DIVIDER_SIZE / 2,
+ DEVICE_WIDTH, DEVICE_LENGTH);
+ mLeftRect.set(0, 0, DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, DEVICE_LENGTH);
+ mRightRect.set(DEVICE_WIDTH / 2 + DIVIDER_SIZE / 2, 0,
+ DEVICE_WIDTH, DEVICE_LENGTH);
+ }
+
+ @Test
+ public void testVerticalStacked() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ TASK_ID_1, TASK_ID_2);
+ assertTrue(ssb.appsStackedVertically);
+ }
+
+ @Test
+ public void testHorizontalStacked() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ TASK_ID_1, TASK_ID_2);
+ assertFalse(ssb.appsStackedVertically);
+ }
+
+ @Test
+ public void testHorizontalDividerBounds() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ TASK_ID_1, TASK_ID_2);
+ Rect dividerBounds = ssb.visualDividerBounds;
+ assertEquals(0, dividerBounds.left);
+ assertEquals(DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2, dividerBounds.top);
+ assertEquals(DEVICE_WIDTH, dividerBounds.right);
+ assertEquals(DEVICE_LENGTH / 2 + DIVIDER_SIZE / 2, dividerBounds.bottom);
+ }
+
+ @Test
+ public void testVerticalDividerBounds() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ TASK_ID_1, TASK_ID_2);
+ Rect dividerBounds = ssb.visualDividerBounds;
+ assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left);
+ assertEquals(0, dividerBounds.top);
+ assertEquals(DEVICE_WIDTH / 2 + DIVIDER_SIZE / 2, dividerBounds.right);
+ assertEquals(DEVICE_LENGTH, dividerBounds.bottom);
+ }
+
+ @Test
+ public void testEqualVerticalTaskPercent() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ TASK_ID_1, TASK_ID_2);
+ float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH;
+ assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01);
+ }
+
+ @Test
+ public void testEqualHorizontalTaskPercent() {
+ StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ TASK_ID_1, TASK_ID_2);
+ float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
+ assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
+ }
+}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index cae2d0bc16b3..5e8a623d4205 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2677,30 +2677,27 @@ bool ResTable_config::isBetterThan(const ResTable_config& o,
// DENSITY_ANY is now dealt with. We should look to
// pick a density bucket and potentially scale it.
// Any density is potentially useful
- // because the system will scale it. Scaling down
- // is generally better than scaling up.
+ // because the system will scale it. Always prefer
+ // scaling down.
int h = thisDensity;
int l = otherDensity;
bool bImBigger = true;
if (l > h) {
- int t = h;
- h = l;
- l = t;
+ std::swap(l, h);
bImBigger = false;
}
- if (requestedDensity >= h) {
- // requested value higher than both l and h, give h
+ if (h == requestedDensity) {
+ // This handles the case where l == h == requestedDensity.
+ // In that case, this and o are equally good so both
+ // true and false are valid. This preserves previous
+ // behavior.
return bImBigger;
- }
- if (l >= requestedDensity) {
+ } else if (l >= requestedDensity) {
// requested value lower than both l and h, give l
return !bImBigger;
- }
- // saying that scaling down is 2x better than up
- if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
- return !bImBigger;
} else {
+ // otherwise give h
return bImBigger;
}
}
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index b54915f03c29..698c36f09301 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -75,6 +75,9 @@ TEST(ConfigTest, shouldSelectBestDensity) {
configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ configs.add(buildDensityConfig(int(ResTable_config::DENSITY_XHIGH) - 1));
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+
expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
configs.add(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 9bbd0a92600b..29ef2b82919d 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -34,6 +34,8 @@ namespace skiapipeline {
*/
class FunctorDrawable : public SkDrawable {
public:
+ constexpr static const char* const TYPE_NAME = "FunctorDrawable";
+
FunctorDrawable(int functor, SkCanvas* canvas)
: mBounds(canvas->getLocalClipBounds())
, mWebViewHandle(WebViewFunctorManager::instance().handleFor(functor)) {}
@@ -48,6 +50,8 @@ public:
mWebViewHandle->onRemovedFromTree();
}
+ const char* getTypeName() const override { return TYPE_NAME; }
+
protected:
virtual SkRect onGetBounds() override { return mBounds; }
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 6777c00c4655..41e36874b862 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include "TransformCanvas.h"
+
+#include "FunctorDrawable.h"
#include "HolePunch.h"
#include "SkData.h"
#include "SkDrawable.h"
@@ -35,7 +37,17 @@ void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkDa
}
void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
- drawable->draw(this, matrix);
+ // TransformCanvas filters all drawing commands while maintaining the current
+ // clip stack and transformation. We need to draw most SkDrawables, since their
+ // draw calls may call methods that affect the clip stack and transformation. (Any
+ // actual draw commands will then be filtered out.) But FunctorDrawables are used
+ // as leaf nodes which issue self-contained OpenGL/Vulkan commands. These won't
+ // affect the clip stack + transformation, and in some cases cause problems (e.g. if
+ // the surface only has an alpha channel). See b/203960959
+ const auto* drawableName = drawable->getTypeName();
+ if (drawableName == nullptr || strcmp(drawableName, FunctorDrawable::TYPE_NAME) != 0) {
+ drawable->draw(this, matrix);
+ }
}
bool TransformCanvas::onFilter(SkPaint& paint) const {
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 052b465f7b84..30278382c8ec 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -278,8 +278,12 @@ Result DvrClient::flush() {
Result DvrClient::close() {
if (mDvrMQEventFlag != nullptr) {
EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+ mDvrMQEventFlag = nullptr;
+ }
+ if (mDvrMQ != nullptr) {
+ delete mDvrMQ;
+ mDvrMQ = nullptr;
}
- mDvrMQ = nullptr;
if (mTunerDvr != nullptr) {
Status s = mTunerDvr->close();
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index fe746fa7971f..e8b3de82f284 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -177,11 +177,14 @@ Result FilterClient::setDataSource(sp<FilterClient> filterClient){
}
Result FilterClient::close() {
- if (mFilterMQEventFlag) {
+ if (mFilterMQEventFlag != nullptr) {
EventFlag::deleteEventFlag(&mFilterMQEventFlag);
+ mFilterMQEventFlag = nullptr;
+ }
+ if (mFilterMQ != nullptr) {
+ delete mFilterMQ;
+ mFilterMQ = nullptr;
}
- mFilterMQEventFlag = nullptr;
- mFilterMQ = nullptr;
if (mTunerFilter != nullptr) {
Status s = mTunerFilter->close();
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index ad62f6e23ef0..a3473459948d 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -18,6 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib">
- <uses-sdk android:minSdkVersion="29" />
-
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 5817f77eb6d9..6e0d82768148 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -18,8 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget">
- <uses-sdk
- android:minSdkVersion="28"
- android:targetSdkVersion="31"/>
-
</manifest>
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index fd89676ab6d7..e9957a9efd38 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -18,6 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.utils">
- <uses-sdk android:minSdkVersion="21" />
-
</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index aede665fe9a7..7e47f4561e78 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -71,6 +71,7 @@ public class InfoMediaManager extends MediaManager {
MediaRouter2Manager mRouterManager;
@VisibleForTesting
String mPackageName;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private MediaDevice mCurrentConnectedDevice;
private LocalBluetoothManager mBluetoothManager;
@@ -84,6 +85,9 @@ public class InfoMediaManager extends MediaManager {
if (!TextUtils.isEmpty(packageName)) {
mPackageName = packageName;
}
+
+ mVolumeAdjustmentForRemoteGroupSessions = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
@Override
@@ -386,7 +390,9 @@ public class InfoMediaManager extends MediaManager {
@TargetApi(Build.VERSION_CODES.R)
boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
- return false;
+ return sessionInfo.isSystemSession() // System sessions are not remote
+ || mVolumeAdjustmentForRemoteGroupSessions
+ || sessionInfo.getSelectedRoutes().size() <= 1;
}
private void refreshDevices() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
index 5252c6c82754..52d243a14e2f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
@@ -20,9 +20,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.shadow.api.Shadow.extract;
-import android.net.ConnectivityManager;
import android.os.UserManager;
-import android.util.SparseBooleanArray;
+import android.telephony.TelephonyManager;
import org.junit.Before;
import org.junit.Test;
@@ -35,7 +34,7 @@ import org.robolectric.annotation.Implements;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
- SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class})
+ SimStatusImeiInfoPreferenceControllerTest.ShadowTelephonyManager.class})
public class SimStatusImeiInfoPreferenceControllerTest {
private AbstractSimStatusImeiInfoPreferenceController mController;
@@ -56,9 +55,9 @@ public class SimStatusImeiInfoPreferenceControllerTest {
ShadowUserManager userManager =
extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
userManager.setIsAdminUser(true);
- ShadowConnectivityManager connectivityManager =
- extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class));
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true);
+ ShadowTelephonyManager telephonyManager =
+ extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class));
+ telephonyManager.setDataCapable(true);
assertThat(mController.isAvailable()).isTrue();
}
@@ -68,9 +67,9 @@ public class SimStatusImeiInfoPreferenceControllerTest {
ShadowUserManager userManager =
extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
userManager.setIsAdminUser(true);
- ShadowConnectivityManager connectivityManager =
- extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class));
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false);
+ ShadowTelephonyManager telephonyManager =
+ extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class));
+ telephonyManager.setDataCapable(false);
assertThat(mController.isAvailable()).isFalse();
}
@@ -99,19 +98,17 @@ public class SimStatusImeiInfoPreferenceControllerTest {
}
}
- @Implements(ConnectivityManager.class)
- public static class ShadowConnectivityManager
- extends org.robolectric.shadows.ShadowConnectivityManager {
-
- private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
-
- private void setNetworkSupported(int networkType, boolean supported) {
- mSupportedNetworkTypes.put(networkType, supported);
+ @Implements(TelephonyManager.class)
+ public static class ShadowTelephonyManager
+ extends org.robolectric.shadows.ShadowTelephonyManager {
+ private boolean mDataCapable = false;
+ private void setDataCapable(boolean capable) {
+ mDataCapable = capable;
}
@Implementation
- public boolean isNetworkSupported(int networkType) {
- return mSupportedNetworkTypes.get(networkType);
+ public boolean isDataCapable() {
+ return mDataCapable;
}
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 93f900d9bda0..2bd5bdc1e7a3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -257,14 +257,6 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.Wearable.SYSTEM_CAPABILITIES, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.SYSTEM_EDITION, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_PLATFORM_MR_NUMBER, ANY_INTEGER_VALIDATOR);
- VALIDATORS.put(Global.Wearable.BOTTOM_OFFSET, ANY_INTEGER_VALIDATOR);
- VALIDATORS.put(
- Global.Wearable.DISPLAY_SHAPE,
- new DiscreteValueValidator(
- new String[] {
- String.valueOf(Global.Wearable.DISPLAY_SHAPE_ROUND),
- String.valueOf(Global.Wearable.DISPLAY_SHAPE_SQUARE)
- }));
VALIDATORS.put(Global.Wearable.MOBILE_SIGNAL_DETECTOR, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.AMBIENT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_WAKE, BOOLEAN_VALIDATOR);
@@ -299,7 +291,6 @@ public class GlobalSettingsValidators {
String.valueOf(Global.Wearable.HFP_CLIENT_ENABLED),
String.valueOf(Global.Wearable.HFP_CLIENT_DISABLED)
}));
- VALIDATORS.put(Global.Wearable.HFP_CLIENT_PROFILE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.COMPANION_OS_VERSION, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.ENABLE_ALL_LANGUAGES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.OEM_SETUP_VERSION, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ea46ef1220cf..cd4047bf48b2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5294,11 +5294,6 @@ public class SettingsProvider extends ContentProvider {
Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
SystemProperties.getInt("ro.cw_build.platform_mr", 0));
initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.BOTTOM_OFFSET, 0);
- initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.DISPLAY_SHAPE,
- Settings.Global.Wearable.DISPLAY_SHAPE_SQUARE);
- initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
getContext()
.getResources()
@@ -5345,8 +5340,6 @@ public class SettingsProvider extends ContentProvider {
Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
SystemProperties.getInt("ro.ambient.plugged_timeout_min", -1));
initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.COMPANION_ADDRESS, "");
- initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN);
initGlobalSettingsDefaultValForWearLocked(
@@ -5359,12 +5352,6 @@ public class SettingsProvider extends ContentProvider {
disabledProfileSetting.isNull()
? 0
: Long.parseLong(disabledProfileSetting.getValue());
- final boolean isHfpClientProfileEnabled =
- (disabledProfileSettingValue & (1 << BluetoothProfile.HEADSET_CLIENT))
- == 0;
- initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
- isHfpClientProfileEnabled);
initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.COMPANION_OS_VERSION,
Settings.Global.Wearable.COMPANION_OS_VERSION_UNDEFINED);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 82012d99bf63..c05e01dda1a5 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -632,9 +632,6 @@ public class SettingsBackupTest {
Settings.Global.Wearable.SYSTEM_CAPABILITIES,
Settings.Global.Wearable.SYSTEM_EDITION,
Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
- Settings.Global.Wearable.COMPANION_BT_ADDRESS_DUAL,
- Settings.Global.Wearable.DISPLAY_SHAPE,
- Settings.Global.Wearable.BOTTOM_OFFSET,
Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
Settings.Global.Wearable.AMBIENT_ENABLED,
@@ -647,12 +644,10 @@ public class SettingsBackupTest {
Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
- Settings.Global.Wearable.COMPANION_ADDRESS,
Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
Settings.Global.Wearable.COMPANION_BLE_ROLE,
Settings.Global.Wearable.COMPANION_NAME,
Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
- Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
Settings.Global.Wearable.COMPANION_OS_VERSION,
Settings.Global.Wearable.ENABLE_ALL_LANGUAGES,
Settings.Global.Wearable.SETUP_LOCALE,
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 1cf14f2362de..ce23a8bc09ca 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -22,6 +22,7 @@ hwwang@google.com
hyunyoungs@google.com
jaggies@google.com
jamesoleary@google.com
+jbolinger@google.com
jdemeulenaere@google.com
jeffdq@google.com
jjaggi@google.com
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 1844288796cc..0b3eccfd3a91 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -85,9 +85,10 @@ public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
val camSeed = Cam.fromInt(seedArgb)
val hue = camSeed.hue
val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
+ val tertiaryHue = wrapDegrees((hue + ACCENT3_HUE_SHIFT).toInt())
accent1 = Shades.of(hue, chroma).toList()
accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
- accent3 = Shades.of(hue + ACCENT3_HUE_SHIFT, ACCENT3_CHROMA).toList()
+ accent3 = Shades.of(tertiaryHue.toFloat(), ACCENT3_CHROMA).toList()
neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
}
diff --git a/packages/SystemUI/plugin/AndroidManifest.xml b/packages/SystemUI/plugin/AndroidManifest.xml
index 7c057dc78ac7..811595ade979 100644
--- a/packages/SystemUI/plugin/AndroidManifest.xml
+++ b/packages/SystemUI/plugin/AndroidManifest.xml
@@ -18,7 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.systemui.plugins">
- <uses-sdk
- android:minSdkVersion="21" />
-
</manifest>
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index a110413700b2..07c2ac426391 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -5,8 +5,8 @@
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
-keep class com.android.systemui.tv.TvSystemUIFactory
--keep class * extends com.android.systemui.SystemUI
--keep class * implements com.android.systemui.SystemUI$Injector
+-keep class * extends com.android.systemui.CoreStartable
+-keep class * implements com.android.systemui.CoreStartable$Injector
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
@@ -22,7 +22,7 @@
}
-keep class androidx.core.app.CoreComponentFactory
--keep public class * extends com.android.systemui.SystemUI {
+-keep public class * extends com.android.systemui.CoreStartable {
public <init>(android.content.Context);
}
diff --git a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
index c415ecd4f0a8..88914ded15c8 100644
--- a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
@@ -20,5 +20,5 @@
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
- android:pathData="M20,6h-4V4c0-1.1-0.9-2-2-2h-4C8.9,2,8,2.9,8,4v2H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8 C22,6.9,21.1,6,20,6z M10,4h4v2h-4V4z M20,19H4V8h16V19z" />
+ android:pathData="@*android:string/config_work_badge_path_24" />
</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 6bc01389e3c3..b2ff46e0de29 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -78,12 +78,6 @@
android:layout_gravity="center_vertical"
android:padding="8dp" />
</com.android.systemui.statusbar.notification.row.AppControlView>
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@*android:color/background_device_default_light"
- />
<!-- ChannelRows get added dynamically -->
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 245d1579d5a3..d03cd7e87a2d 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -87,10 +87,4 @@
android:padding="8dp"
/>
</LinearLayout>
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height=".5dp"
- android:background="@*android:color/background_device_default_light"
- />
</com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d2ea78978ba2..af5d85de0562 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -77,7 +77,7 @@
<!-- The color of the gear shown behind a notification -->
<color name="notification_gear_color">@color/GM2_grey_700</color>
- <color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color>
+ <color name="notification_guts_link_icon_tint">@*android:color/accent_device_default</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_700</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
index ee6dea5364f4..91a391272be7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -20,6 +20,11 @@ package com.android.systemui.flags
*/
interface FlagReader {
/** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: BooleanFlag): Boolean {
+ return flag.default
+ }
+
+ /** Returns a boolean value for the given flag. */
fun isEnabled(id: Int, def: Boolean): Boolean {
return def
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java
deleted file mode 100644
index 323b20e41a5c..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/GroupTask.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.recents.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * A group task in the recent tasks list.
- * TODO: Move this into Launcher
- */
-public class GroupTask {
- public @NonNull Task task1;
- public @Nullable Task task2;
-
- public GroupTask(@NonNull Task t1, @Nullable Task t2) {
- task1 = t1;
- task2 = t2;
- }
-
- public GroupTask(@NonNull GroupTask group) {
- task1 = new Task(group.task1);
- task2 = group.task2 != null
- ? new Task(group.task2)
- : null;
- }
-
- public boolean containsTask(int taskId) {
- return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index b95123d2fa41..38eded878014 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -195,8 +195,13 @@ public class ActivityManagerWrapper {
}
@Override
- public void onTaskAppeared(RemoteAnimationTarget app) {
- animationHandler.onTaskAppeared(new RemoteAnimationTargetCompat(app));
+ public void onTasksAppeared(RemoteAnimationTarget[] apps) {
+ final RemoteAnimationTargetCompat[] compats =
+ new RemoteAnimationTargetCompat[apps.length];
+ for (int i = 0; i < apps.length; ++i) {
+ compats[i] = new RemoteAnimationTargetCompat(apps[i]);
+ }
+ animationHandler.onTasksAppeared(compats);
}
};
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index a74de2e0c085..48f1b76c1d50 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -39,5 +39,5 @@ public interface RecentsAnimationListener {
* Called when the task of an activity that has been started while the recents animation
* was running becomes ready for control.
*/
- void onTaskAppeared(RemoteAnimationTargetCompat app);
+ void onTasksAppeared(RemoteAnimationTargetCompat[] app);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 99b6aed497cc..954cf9fd81a8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -53,6 +53,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import java.util.ArrayList;
import java.util.concurrent.Executor;
/**
@@ -127,7 +128,7 @@ public class RemoteTransitionCompat implements Parcelable {
mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
// the current going-away task on top of recents, though, so move it to front
- WindowContainerToken pausingTask = null;
+ final ArrayList<WindowContainerToken> pausingTasks = new ArrayList<>();
WindowContainerToken pipTask = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -138,7 +139,8 @@ public class RemoteTransitionCompat implements Parcelable {
if (taskInfo == null) {
continue;
}
- pausingTask = taskInfo.token;
+ // Add to front since we are iterating backwards.
+ pausingTasks.add(0, taskInfo.token);
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
pipTask = taskInfo.token;
@@ -150,7 +152,7 @@ public class RemoteTransitionCompat implements Parcelable {
t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
}
t.apply();
- mRecentsSession.setup(controller, info, finishedCallback, pausingTask, pipTask,
+ mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
leashMap, mToken);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
@@ -198,18 +200,18 @@ public class RemoteTransitionCompat implements Parcelable {
static class RecentsControllerWrap extends RecentsAnimationControllerCompat {
private RecentsAnimationControllerCompat mWrapped = null;
private IRemoteTransitionFinishedCallback mFinishCB = null;
- private WindowContainerToken mPausingTask = null;
+ private ArrayList<WindowContainerToken> mPausingTasks = null;
private WindowContainerToken mPipTask = null;
private TransitionInfo mInfo = null;
- private SurfaceControl mOpeningLeash = null;
+ private ArrayList<SurfaceControl> mOpeningLeashes = null;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
private PictureInPictureSurfaceTransaction mPipTransaction = null;
private IBinder mTransition = null;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
- IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
- WindowContainerToken pipTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
- IBinder transition) {
+ IRemoteTransitionFinishedCallback finishCB,
+ ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask,
+ ArrayMap<SurfaceControl, SurfaceControl> leashMap, IBinder transition) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -217,7 +219,7 @@ public class RemoteTransitionCompat implements Parcelable {
mWrapped = wrapped;
mInfo = info;
mFinishCB = finishCB;
- mPausingTask = pausingTask;
+ mPausingTasks = pausingTasks;
mPipTask = pipTask;
mLeashMap = leashMap;
mTransition = transition;
@@ -226,36 +228,57 @@ public class RemoteTransitionCompat implements Parcelable {
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
RecentsAnimationListener recents) {
- TransitionInfo.Change openingTask = null;
+ ArrayList<TransitionInfo.Change> openingTasks = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
if (change.getTaskInfo() != null) {
- if (openingTask != null) {
- Log.w(TAG, " Expecting to merge a task-open, but got >1 opening "
- + "tasks");
+ if (openingTasks == null) {
+ openingTasks = new ArrayList<>();
}
- openingTask = change;
+ openingTasks.add(change);
}
}
}
- if (openingTask == null) return false;
- mOpeningLeash = openingTask.getLeash();
- if (openingTask.getContainer().equals(mPausingTask)) {
- // In this case, we are "returning" to the already running app, so just consume
+ if (openingTasks == null) return false;
+ int pauseMatches = 0;
+ for (int i = 0; i < openingTasks.size(); ++i) {
+ if (mPausingTasks.contains(openingTasks.get(i).getContainer())) {
+ ++pauseMatches;
+ }
+ if (openingTasks.get(i).getContainer().equals(mPausingTasks.get(i))) {
+ // In this case, we are "returning" to an already running app, so just consume
+ // the merge and do nothing.
+ }
+ }
+ if (pauseMatches > 0) {
+ if (pauseMatches != mPausingTasks.size()) {
+ // We are not really "returning" properly... something went wrong.
+ throw new IllegalStateException("\"Concelling\" a recents transitions by "
+ + "unpausing " + pauseMatches + " apps after pausing "
+ + mPausingTasks.size() + " apps.");
+ }
+ // In this case, we are "returning" to an already running app, so just consume
// the merge and do nothing.
return true;
}
- // We are receiving a new opening task, so convert to onTaskAppeared.
final int layer = mInfo.getChanges().size() * 3;
- final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
- openingTask, layer, mInfo, t);
- mLeashMap.put(mOpeningLeash, target.leash.mSurfaceControl);
- t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
- t.setLayer(target.leash.mSurfaceControl, layer);
- t.hide(target.leash.mSurfaceControl);
- t.apply();
- recents.onTaskAppeared(target);
+ mOpeningLeashes = new ArrayList<>();
+ final RemoteAnimationTargetCompat[] targets =
+ new RemoteAnimationTargetCompat[openingTasks.size()];
+ for (int i = 0; i < openingTasks.size(); ++i) {
+ mOpeningLeashes.add(openingTasks.get(i).getLeash());
+ // We are receiving new opening tasks, so convert to onTasksAppeared.
+ final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
+ openingTasks.get(i), layer, mInfo, t);
+ mLeashMap.put(mOpeningLeashes.get(i), target.leash.mSurfaceControl);
+ t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
+ t.setLayer(target.leash.mSurfaceControl, layer);
+ t.hide(target.leash.mSurfaceControl);
+ t.apply();
+ targets[i] = target;
+ }
+ recents.onTasksAppeared(targets);
return true;
}
@@ -292,21 +315,26 @@ public class RemoteTransitionCompat implements Parcelable {
}
if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
try {
- if (!toHome && mPausingTask != null && mOpeningLeash == null) {
+ if (!toHome && mPausingTasks != null && mOpeningLeashes == null) {
// The gesture went back to opening the app rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reorder(mPausingTask, true /* onTop */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(mInfo.getChange(mPausingTask).getLeash());
+ for (int i = mPausingTasks.size() - 1; i >= 0; ++i) {
+ // reverse order so that index 0 ends up on top
+ wct.reorder(mPausingTasks.get(i), true /* onTop */);
+ t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
+ }
mFinishCB.onTransitionFinished(wct, t);
} else {
- if (mOpeningLeash != null) {
+ if (mOpeningLeashes != null) {
// TODO: the launcher animation should handle this
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(mOpeningLeash);
- t.setAlpha(mOpeningLeash, 1.f);
+ for (int i = 0; i < mOpeningLeashes.size(); ++i) {
+ t.show(mOpeningLeashes.get(i));
+ t.setAlpha(mOpeningLeashes.get(i), 1.f);
+ }
t.apply();
}
if (mPipTask != null && mPipTransaction != null) {
@@ -339,9 +367,9 @@ public class RemoteTransitionCompat implements Parcelable {
// Reset all members.
mWrapped = null;
mFinishCB = null;
- mPausingTask = null;
+ mPausingTasks = null;
mInfo = null;
- mOpeningLeash = null;
+ mOpeningLeashes = null;
mLeashMap = null;
mTransition = null;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index ef046193473d..acfa3c84a4ba 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -26,13 +26,16 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
+import androidx.annotation.BoolRes;
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.settings.SecureSettings;
@@ -62,14 +65,19 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
+ private final Resources mResources;
private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
@Inject
- public FeatureFlagManager(FlagManager flagManager,
- SecureSettings secureSettings, Context context,
+ public FeatureFlagManager(
+ FlagManager flagManager,
+ Context context,
+ SecureSettings secureSettings,
+ @Main Resources resources,
DumpManager dumpManager) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
+ mResources = resources;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
@@ -77,17 +85,32 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
dumpManager.registerDumpable(TAG, this);
}
- /** Return a {@link BooleanFlag}'s value. */
@Override
- public boolean isEnabled(int id, boolean defaultValue) {
+ public boolean isEnabled(BooleanFlag flag) {
+ int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- Boolean result = isEnabledInternal(id);
- mBooleanFlagCache.put(id, result == null ? defaultValue : result);
+ boolean def = flag.getDefault();
+ if (flag.hasResourceOverride()) {
+ try {
+ def = isEnabledInOverlay(flag.getResourceOverride());
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ }
+
+ mBooleanFlagCache.put(id, isEnabled(id, def));
}
return mBooleanFlagCache.get(id);
}
+ /** Return a {@link BooleanFlag}'s value. */
+ @Override
+ public boolean isEnabled(int id, boolean defaultValue) {
+ Boolean result = isEnabledInternal(id);
+ return result == null ? defaultValue : result;
+ }
+
/** Returns the stored value or null if not set. */
private Boolean isEnabledInternal(int id) {
try {
@@ -98,6 +121,10 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
return null;
}
+ private boolean isEnabledInOverlay(@BoolRes int resId) {
+ return mResources.getBoolean(resId);
+ }
+
/** Set whether a given {@link BooleanFlag} is enabled or not. */
@Override
public void setEnabled(int id, boolean value) {
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
index 6ff175f589a6..0934b32a71e4 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.flags;
-import android.content.Context;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
@@ -24,7 +23,6 @@ import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -41,8 +39,7 @@ import javax.inject.Inject;
public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
@Inject
- public FeatureFlagManager(
- SecureSettings secureSettings, Context context, DumpManager dumpManager) {
+ public FeatureFlagManager(DumpManager dumpManager) {
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -53,6 +50,11 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
public void removeListener(Listener run) {}
@Override
+ public boolean isEnabled(BooleanFlag flag) {
+ return isEnabled(flag.getId(), flag.getDefault());
+ }
+
+ @Override
public boolean isEnabled(int key, boolean defaultValue) {
mAccessedFlags.append(key, defaultValue);
return defaultValue;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 85bc8f7c70a2..d27bc675ecb8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2044,17 +2044,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
- * @return true if there's at least one udfps enrolled
+ * @return true if there's at least one udfps enrolled for the current user.
*/
public boolean isUdfpsEnrolled() {
return mIsUdfpsEnrolled;
}
/**
- * @return if udfps is available on this device. will return true even if the user hasn't
- * enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
+ * @return true if udfps HW is supported on this device. Can return true even if the user has
+ * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
- public boolean isUdfpsAvailable() {
+ public boolean isUdfpsSupported() {
return mAuthController.getUdfpsProps() != null
&& !mAuthController.getUdfpsProps().isEmpty();
}
@@ -2102,7 +2102,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
updateUdfpsEnrolled(getCurrentUser());
- final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsEnrolled());
+ final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
if (runningOrRestarting && !shouldListenForFingerprint) {
@@ -2407,7 +2407,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
} else {
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback, null /* handler */,
- FingerprintManager.SENSOR_ID_ANY, userId);
+ FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */);
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
@@ -2990,7 +2990,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* Register to receive notifications about general keyguard information
- * (see {@link InfoCallback}.
+ * (see {@link KeyguardUpdateMonitorCallback}.
*
* @param callback The callback to register
*/
@@ -3388,11 +3388,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
+ " expected=" + (shouldListenForFingerprint(isUdfpsEnrolled()) ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
- pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut);
pw.println(" mFingerprintLockedOutPermanent=" + mFingerprintLockedOutPermanent);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
- if (isUdfpsEnrolled()) {
+ if (isUdfpsSupported()) {
+ pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
pw.println(" bouncerVisible=" + mBouncer);
pw.println(" mStatusBarState="
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 23438a957b48..a382b5331d26 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -136,6 +136,13 @@ public class KeyguardVisibilityHelper {
.setStartDelay(delay);
}
animator.start();
+ } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
+ mKeyguardViewVisibilityAnimating = true;
+
+ // 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.
+ mUnlockedScreenOffAnimationController.animateInKeyguard(
+ mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else if (mLastOccludedState && !isOccluded) {
// An activity was displayed over the lock screen, and has now gone away
mView.setVisibility(View.VISIBLE);
@@ -147,13 +154,6 @@ public class KeyguardVisibilityHelper {
.alpha(1f)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
.start();
- } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
- mKeyguardViewVisibilityAnimating = true;
-
- // 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.
- mUnlockedScreenOffAnimationController.animateInKeyguard(
- mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else {
mView.setVisibility(View.VISIBLE);
mView.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8a0b5b8704e6..c7be3ce01c54 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -435,7 +435,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
boolean wasUdfpsSupported = mUdfpsSupported;
boolean wasUdfpsEnrolled = mUdfpsEnrolled;
- mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+ mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
mView.setUseBackground(mUdfpsSupported);
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index c6a750af862c..0df8e0228984 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -30,17 +30,18 @@ import java.io.PrintWriter;
/**
* A top-level module of system UI code (sometimes called "system UI services" elsewhere in code).
- * Which SystemUI modules are loaded can be controlled via a config resource.
+ * Which CoreStartable modules are loaded can be controlled via a config resource.
*
* @see SystemUIApplication#startServicesIfNeeded()
*/
-public abstract class SystemUI implements Dumpable {
+public abstract class CoreStartable implements Dumpable {
protected final Context mContext;
- public SystemUI(Context context) {
+ public CoreStartable(Context context) {
mContext = context;
}
+ /** Main entry point for implementations. Called shortly after app startup. */
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
@@ -54,6 +55,7 @@ public abstract class SystemUI implements Dumpable {
protected void onBootCompleted() {
}
+ /** TODO(b/205725937): Move this. SystemUIApplication? */
public static void overrideNotificationAppName(Context context, Notification.Builder n,
boolean system) {
final Bundle extras = new Bundle();
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index d325b92cf89f..bc2a1ff24235 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -35,7 +35,7 @@ import javax.inject.Inject;
* system that are used for testing the latency.
*/
@SysUISingleton
-public class LatencyTester extends SystemUI {
+public class LatencyTester extends CoreStartable {
private static final String
ACTION_FINGERPRINT_WAKE =
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index b2fae9dd8469..e84024d4f88f 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -107,7 +107,7 @@ import javax.inject.Inject;
* for antialiasing and emulation purposes.
*/
@SysUISingleton
-public class ScreenDecorations extends SystemUI implements Tunable {
+public class ScreenDecorations extends CoreStartable implements Tunable {
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index 0b997d0e0955..d7da63b9e262 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -38,7 +38,7 @@ import javax.inject.Inject;
* @see SliceBroadcastRelay
*/
@SysUISingleton
-public class SliceBroadcastRelayHandler extends SystemUI {
+public class SliceBroadcastRelayHandler extends CoreStartable {
private static final String TAG = "SliceBroadcastRelay";
private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4fd270131466..61d8c25f63ff 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -60,7 +60,7 @@ public class SystemUIApplication extends Application implements
/**
* Hold a reference on the stuff we start.
*/
- private SystemUI[] mServices;
+ private CoreStartable[] mServices;
private boolean mServicesStarted;
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
private GlobalRootComponent mRootComponent;
@@ -190,7 +190,7 @@ public class SystemUIApplication extends Application implements
if (mServicesStarted) {
return;
}
- mServices = new SystemUI[services.length];
+ mServices = new CoreStartable[services.length];
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
@@ -217,10 +217,10 @@ public class SystemUIApplication extends Application implements
log.traceBegin(metricsPrefix + clsName);
long ti = System.currentTimeMillis();
try {
- SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
+ CoreStartable obj = mComponentHelper.resolveCoreStartable(clsName);
if (obj == null) {
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
- obj = (SystemUI) constructor.newInstance(this);
+ obj = (CoreStartable) constructor.newInstance(this);
}
mServices[i] = obj;
} catch (ClassNotFoundException
@@ -265,7 +265,7 @@ public class SystemUIApplication extends Application implements
}
}
- public SystemUI[] getServices() {
+ public CoreStartable[] getServices() {
return mServices;
}
diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java
index 13d847bf93c4..139448c0fab4 100644
--- a/packages/SystemUI/src/com/android/systemui/VendorServices.java
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -21,7 +21,7 @@ import android.content.Context;
/**
* Placeholder for any vendor-specific services.
*/
-public class VendorServices extends SystemUI {
+public class VendorServices extends CoreStartable {
public VendorServices(Context context) {
super(context);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index e521c90961fb..052ec86d8398 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -28,7 +28,6 @@ import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import android.view.Gravity;
@@ -75,6 +74,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
private final LayoutParams mParams;
+ private final SwitchListener mSwitchListener;
@VisibleForTesting
final Rect mDraggableWindowBounds = new Rect();
private boolean mIsVisible = false;
@@ -82,17 +82,29 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private boolean mSingleTapDetected = false;
private boolean mToLeftScreenEdge = false;
- MagnificationModeSwitch(@UiContext Context context) {
- this(context, createView(context), new SfVsyncFrameCallbackProvider());
+ public interface SwitchListener {
+ /**
+ * Called when the switch is clicked to change the magnification mode.
+ * @param displayId the display id of the display to which the view's window has been
+ * attached
+ * @param magnificationMode the magnification mode
+ */
+ void onSwitch(int displayId, int magnificationMode);
+ }
+
+ MagnificationModeSwitch(@UiContext Context context,
+ SwitchListener switchListener) {
+ this(context, createView(context), new SfVsyncFrameCallbackProvider(), switchListener);
}
@VisibleForTesting
MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
- SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
+ SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SwitchListener switchListener) {
mContext = context;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = mContext.getSystemService(WindowManager.class);
mSfVsyncFrameProvider = sfVsyncFrameProvider;
+ mSwitchListener = switchListener;
mParams = createLayoutParams(context);
mImageView = imageView;
mImageView.setOnTouchListener(this::onTouch);
@@ -364,11 +376,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mMagnificationMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
mMagnificationMode = newMode;
mImageView.setImageResource(getIconResId(newMode));
- Settings.Secure.putIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
- newMode,
- UserHandle.USER_CURRENT);
+ mSwitchListener.onSwitch(mContext.getDisplayId(), newMode);
}
private void handleSingleTap() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index 1a01ad85fccc..5e48ee3d2366 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -18,6 +18,8 @@ package com.android.systemui.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static com.android.systemui.accessibility.MagnificationModeSwitch.SwitchListener;
+
import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -27,21 +29,24 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
/**
- * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * A class to control {@link MagnificationModeSwitch}. It shows the button UI with following
* conditions:
* <ol>
* <li> Both full-screen and window magnification mode are capable.</li>
* <li> The magnification scale is changed by a user.</li>
* <ol>
+ * The switch action will be handled by {@link #mSwitchListenerDelegate} which informs the system
+ * server about the changed mode.
*/
@SysUISingleton
-public class ModeSwitchesController {
+public class ModeSwitchesController implements SwitchListener {
private final DisplayIdIndexSupplier<MagnificationModeSwitch> mSwitchSupplier;
+ private SwitchListener mSwitchListenerDelegate;
public ModeSwitchesController(Context context) {
mSwitchSupplier = new SwitchSupplier(context,
- context.getSystemService(DisplayManager.class));
+ context.getSystemService(DisplayManager.class), this::onSwitch);
}
@VisibleForTesting
@@ -50,8 +55,8 @@ public class ModeSwitchesController {
}
/**
- * Shows a button that a user can click the button to switch magnification mode. And the
- * button would be dismissed automatically after the button is displayed for a period of time.
+ * Shows a button that a user can click to switch magnification mode. And the button
+ * would be dismissed automatically after the button is displayed for a period of time.
*
* @param displayId The logical display id
* @param mode The magnification mode
@@ -93,24 +98,41 @@ public class ModeSwitchesController {
switchController -> switchController.onConfigurationChanged(configDiff));
}
+ @Override
+ public void onSwitch(int displayId, int magnificationMode) {
+ if (mSwitchListenerDelegate != null) {
+ mSwitchListenerDelegate.onSwitch(displayId, magnificationMode);
+ }
+ }
+
+ public void setSwitchListenerDelegate(SwitchListener switchListenerDelegate) {
+ mSwitchListenerDelegate = switchListenerDelegate;
+ }
+
private static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
private final Context mContext;
+ private final SwitchListener mSwitchListener;
/**
+ * Supplies the switch for the given display.
+ *
* @param context Context
* @param displayManager DisplayManager
+ * @param switchListener The callback that will run when the switch is clicked
*/
- SwitchSupplier(Context context, DisplayManager displayManager) {
+ SwitchSupplier(Context context, DisplayManager displayManager,
+ SwitchListener switchListener) {
super(displayManager);
mContext = context;
+ mSwitchListener = switchListener;
}
@Override
protected MagnificationModeSwitch createInstance(Display display) {
final Context uiContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
- return new MagnificationModeSwitch(uiContext);
+ return new MagnificationModeSwitch(uiContext, mSwitchListener);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 294d1f42305a..20d6e3247cd1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -49,7 +49,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.util.ScreenshotHelper;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.CommandQueue;
@@ -69,7 +69,7 @@ import dagger.Lazy;
* Class to register system actions with accessibility framework.
*/
@SysUISingleton
-public class SystemActions extends SystemUI {
+public class SystemActions extends CoreStartable {
private static final String TAG = "SystemActions";
/**
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 32813479dc0a..33ce20686e66 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -35,7 +35,7 @@ import android.view.accessibility.IWindowMagnificationConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
@@ -54,7 +54,7 @@ import javax.inject.Inject;
* when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
*/
@SysUISingleton
-public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
+public class WindowMagnification extends CoreStartable implements WindowMagnifierCallback,
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
@@ -243,12 +243,15 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
mHandler, mModeSwitchesController);
}
+ mModeSwitchesController.setSwitchListenerDelegate(
+ mWindowMagnificationConnectionImpl::onChangeMagnificationMode);
mAccessibilityManager.setWindowMagnificationConnection(
mWindowMagnificationConnectionImpl);
}
private void clearWindowMagnificationConnection() {
mAccessibilityManager.setWindowMagnificationConnection(null);
+ mModeSwitchesController.setSwitchListenerDelegate(null);
//TODO: destroy controllers.
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index 2d620ab9e3c9..92cd8b183b62 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -130,4 +130,14 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
}
}
}
+
+ void onChangeMagnificationMode(int displayId, int mode) {
+ if (mConnectionCallback != null) {
+ try {
+ mConnectionCallback.onChangeMagnificationMode(displayId, mode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to inform changing magnification mode", e);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 59d9aff2ef46..d2703f5e73a2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -22,6 +22,7 @@ import static android.util.MathUtils.sq;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static java.util.Objects.requireNonNull;
@@ -659,6 +660,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.receiveInsetsIgnoringZOrder = true;
+ params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
params.windowAnimations = android.R.style.Animation_Translucent;
params.gravity = Gravity.START | Gravity.TOP;
params.x = (mAlignment == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
index 376368fbf9d4..d80d9cc9d62d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
@@ -21,12 +21,19 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
/**
* Manages the layout for under-display fingerprint sensors (UDFPS). Ensures that UI elements
* do not overlap with
*/
public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
+ private static final String TAG = "AuthBiometricUdfpsView";
+
@Nullable private UdfpsDialogMeasureAdapter mMeasureAdapter;
public AuthBiometricUdfpsView(Context context) {
@@ -51,4 +58,23 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
? mMeasureAdapter.onMeasureInternal(width, height, layoutParams)
: layoutParams;
}
+
+ @Override
+ void onLayoutInternal() {
+ super.onLayoutInternal();
+
+ // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen
+ // for devices where the UDFPS sensor is too low.
+ // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap
+ // the button bar area.
+ final int bottomSpacerHeight = mMeasureAdapter.getBottomSpacerHeight();
+ Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight);
+ if (bottomSpacerHeight < 0) {
+ FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame);
+ iconFrame.setTranslationY(-bottomSpacerHeight);
+
+ TextView indicator = findViewById(R.id.indicator);
+ indicator.setTranslationY(-bottomSpacerHeight);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 7215736330ed..29e5574830a8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -58,7 +58,7 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.assist.ui.DisplayUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -84,7 +84,7 @@ import kotlin.Unit;
* {@link com.android.keyguard.KeyguardUpdateMonitor}
*/
@SysUISingleton
-public class AuthController extends SystemUI implements CommandQueue.Callbacks,
+public class AuthController extends CoreStartable implements CommandQueue.Callbacks,
AuthDialogCallback, DozeReceiver {
private static final String TAG = "AuthController";
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index ec17d4e5cd03..90a1e5e64daf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.PointF
@@ -29,7 +31,9 @@ import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
@@ -41,13 +45,10 @@ import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarS
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
+import com.android.systemui.util.leak.RotationUtils
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.util.leak.RotationUtils
-
-private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L
/***
* Controls the ripple effect that shows when authentication is successful.
@@ -141,11 +142,12 @@ class AuthRippleController @Inject constructor(
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val useCircleReveal = circleReveal != null && biometricUnlockController.isWakeAndUnlock
val lightRevealScrim = statusBar.lightRevealScrim
- if (useCircleReveal) {
- lightRevealScrim?.revealEffect = circleReveal!!
- startLightRevealScrimOnKeyguardFadingAway = true
+ if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+ circleReveal?.let {
+ lightRevealScrim?.revealEffect = it
+ startLightRevealScrimOnKeyguardFadingAway = true
+ }
}
mView.startUnlockedRipple(
@@ -160,19 +162,29 @@ class AuthRippleController @Inject constructor(
if (keyguardStateController.isKeyguardFadingAway) {
val lightRevealScrim = statusBar.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
- val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
+ ValueAnimator.ofFloat(.1f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
duration = RIPPLE_ANIMATION_DURATION
startDelay = keyguardStateController.keyguardFadingAwayDelay
addUpdateListener { animator ->
if (lightRevealScrim.revealEffect != circleReveal) {
- // if the something else took over the reveal, let's do nothing.
+ // if something else took over the reveal, let's do nothing.
return@addUpdateListener
}
lightRevealScrim.revealAmount = animator.animatedValue as Float
}
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ // Reset light reveal scrim to the default, so the StatusBar
+ // can handle any subsequent light reveal changes
+ // (ie: from dozing changes)
+ if (lightRevealScrim.revealEffect == circleReveal) {
+ lightRevealScrim.revealEffect = LiftReveal
+ }
+ }
+ })
+ start()
}
- revealAnimator.start()
startLightRevealScrimOnKeyguardFadingAway = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 6cc8acf07c50..1624d5f68c35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -46,6 +46,7 @@ public class UdfpsDialogMeasureAdapter {
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@Nullable private WindowManager mWindowManager;
+ private int mBottomSpacerHeight;
public UdfpsDialogMeasureAdapter(
@NonNull ViewGroup view, @NonNull FingerprintSensorPropertiesInternal sensorProps) {
@@ -75,6 +76,16 @@ public class UdfpsDialogMeasureAdapter {
}
}
+ /**
+ * @return the actual (and possibly negative) bottom spacer height. If negative, this indicates
+ * that the UDFPS sensor is too low. Our current xml and custom measurement logic is very hard
+ * too cleanly support this case. So, let's have the onLayout code translate the sensor location
+ * instead.
+ */
+ int getBottomSpacerHeight() {
+ return mBottomSpacerHeight;
+ }
+
@NonNull
private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height) {
// Get the height of the everything below the icon. Currently, that's the indicator and
@@ -87,7 +98,7 @@ public class UdfpsDialogMeasureAdapter {
final int dialogMargin = getDialogMarginPx();
final int displayHeight = getWindowBounds().height();
final Insets navbarInsets = getNavbarInsets();
- final int bottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
+ mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
dialogMargin, navbarInsets.bottom);
@@ -123,9 +134,10 @@ public class UdfpsDialogMeasureAdapter {
MeasureSpec.EXACTLY));
} else if (child.getId() == R.id.space_below_icon) {
// Set the spacer height so the fingerprint icon is on the physical sensor area
+ final int clampedSpacerHeight = Math.max(mBottomSpacerHeight, 0);
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY));
} else {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
index 1e7449c64182..f53221c959a5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
@@ -20,7 +20,7 @@ import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.recents.RecentsImplementation;
/**
@@ -37,7 +37,7 @@ public interface ContextComponentHelper {
Service resolveService(String className);
/** Turns a classname into an instance of the class or returns null. */
- SystemUI resolveSystemUI(String className);
+ CoreStartable resolveCoreStartable(String className);
/** Turns a classname into an instance of the class or returns null. */
BroadcastReceiver resolveBroadcastReceiver(String className);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
index b41915bc2547..fba8d351e990 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
@@ -20,7 +20,7 @@ import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.recents.RecentsImplementation;
import java.util.Map;
@@ -35,14 +35,14 @@ import javax.inject.Provider;
public class ContextComponentResolver implements ContextComponentHelper {
private final Map<Class<?>, Provider<Activity>> mActivityCreators;
private final Map<Class<?>, Provider<Service>> mServiceCreators;
- private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
+ private final Map<Class<?>, Provider<CoreStartable>> mSystemUICreators;
private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;
@Inject
ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
Map<Class<?>, Provider<Service>> serviceCreators,
- Map<Class<?>, Provider<SystemUI>> systemUICreators,
+ Map<Class<?>, Provider<CoreStartable>> systemUICreators,
Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
mActivityCreators = activityCreators;
@@ -88,7 +88,7 @@ public class ContextComponentResolver implements ContextComponentHelper {
* Looks up the SystemUI class name to see if Dagger has an instance of it.
*/
@Override
- public SystemUI resolveSystemUI(String className) {
+ public CoreStartable resolveCoreStartable(String className) {
return resolve(className, mSystemUICreators);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index a5d4d80598c4..e5c6ab547999 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,10 +16,10 @@
package com.android.systemui.dagger;
+import com.android.systemui.CoreStartable;
import com.android.systemui.LatencyTester;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SliceBroadcastRelayHandler;
-import com.android.systemui.SystemUI;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
@@ -63,145 +63,145 @@ public abstract class SystemUIBinder {
@Binds
@IntoMap
@ClassKey(AuthController.class)
- public abstract SystemUI bindAuthController(AuthController service);
+ public abstract CoreStartable bindAuthController(AuthController service);
/** Inject into GarbageMonitor.Service. */
@Binds
@IntoMap
@ClassKey(GarbageMonitor.Service.class)
- public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service sysui);
+ public abstract CoreStartable bindGarbageMonitorService(GarbageMonitor.Service sysui);
/** Inject into GlobalActionsComponent. */
@Binds
@IntoMap
@ClassKey(GlobalActionsComponent.class)
- public abstract SystemUI bindGlobalActionsComponent(GlobalActionsComponent sysui);
+ public abstract CoreStartable bindGlobalActionsComponent(GlobalActionsComponent sysui);
/** Inject into InstantAppNotifier. */
@Binds
@IntoMap
@ClassKey(InstantAppNotifier.class)
- public abstract SystemUI bindInstantAppNotifier(InstantAppNotifier sysui);
+ public abstract CoreStartable bindInstantAppNotifier(InstantAppNotifier sysui);
/** Inject into KeyguardViewMediator. */
@Binds
@IntoMap
@ClassKey(KeyguardViewMediator.class)
- public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+ public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);
/** Inject into LatencyTests. */
@Binds
@IntoMap
@ClassKey(LatencyTester.class)
- public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+ public abstract CoreStartable bindLatencyTester(LatencyTester sysui);
/** Inject into PowerUI. */
@Binds
@IntoMap
@ClassKey(PowerUI.class)
- public abstract SystemUI bindPowerUI(PowerUI sysui);
+ public abstract CoreStartable bindPowerUI(PowerUI sysui);
/** Inject into Recents. */
@Binds
@IntoMap
@ClassKey(Recents.class)
- public abstract SystemUI bindRecents(Recents sysui);
+ public abstract CoreStartable bindRecents(Recents sysui);
/** Inject into ScreenDecorations. */
@Binds
@IntoMap
@ClassKey(ScreenDecorations.class)
- public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+ public abstract CoreStartable bindScreenDecorations(ScreenDecorations sysui);
/** Inject into ShortcutKeyDispatcher. */
@Binds
@IntoMap
@ClassKey(ShortcutKeyDispatcher.class)
- public abstract SystemUI bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
+ public abstract CoreStartable bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
/** Inject into SliceBroadcastRelayHandler. */
@Binds
@IntoMap
@ClassKey(SliceBroadcastRelayHandler.class)
- public abstract SystemUI bindSliceBroadcastRelayHandler(SliceBroadcastRelayHandler sysui);
+ public abstract CoreStartable bindSliceBroadcastRelayHandler(SliceBroadcastRelayHandler sysui);
/** Inject into StatusBar. */
@Binds
@IntoMap
@ClassKey(StatusBar.class)
- public abstract SystemUI bindsStatusBar(StatusBar sysui);
+ public abstract CoreStartable bindsStatusBar(StatusBar sysui);
/** Inject into SystemActions. */
@Binds
@IntoMap
@ClassKey(SystemActions.class)
- public abstract SystemUI bindSystemActions(SystemActions sysui);
+ public abstract CoreStartable bindSystemActions(SystemActions sysui);
/** Inject into ThemeOverlayController. */
@Binds
@IntoMap
@ClassKey(ThemeOverlayController.class)
- public abstract SystemUI bindThemeOverlayController(ThemeOverlayController sysui);
+ public abstract CoreStartable bindThemeOverlayController(ThemeOverlayController sysui);
/** Inject into ToastUI. */
@Binds
@IntoMap
@ClassKey(ToastUI.class)
- public abstract SystemUI bindToastUI(ToastUI service);
+ public abstract CoreStartable bindToastUI(ToastUI service);
/** Inject into TvStatusBar. */
@Binds
@IntoMap
@ClassKey(TvStatusBar.class)
- public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
+ public abstract CoreStartable bindsTvStatusBar(TvStatusBar sysui);
/** Inject into TvNotificationPanel. */
@Binds
@IntoMap
@ClassKey(TvNotificationPanel.class)
- public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui);
+ public abstract CoreStartable bindsTvNotificationPanel(TvNotificationPanel sysui);
/** Inject into TvOngoingPrivacyChip. */
@Binds
@IntoMap
@ClassKey(TvOngoingPrivacyChip.class)
- public abstract SystemUI bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
+ public abstract CoreStartable bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
/** Inject into VolumeUI. */
@Binds
@IntoMap
@ClassKey(VolumeUI.class)
- public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+ public abstract CoreStartable bindVolumeUI(VolumeUI sysui);
/** Inject into WindowMagnification. */
@Binds
@IntoMap
@ClassKey(WindowMagnification.class)
- public abstract SystemUI bindWindowMagnification(WindowMagnification sysui);
+ public abstract CoreStartable bindWindowMagnification(WindowMagnification sysui);
/** Inject into WMShell. */
@Binds
@IntoMap
@ClassKey(WMShell.class)
- public abstract SystemUI bindWMShell(WMShell sysui);
+ public abstract CoreStartable bindWMShell(WMShell sysui);
/** Inject into HomeSoundEffectController. */
@Binds
@IntoMap
@ClassKey(HomeSoundEffectController.class)
- public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
+ public abstract CoreStartable bindHomeSoundEffectController(HomeSoundEffectController sysui);
/** Inject into DreamOverlay. */
@Binds
@IntoMap
@ClassKey(DreamOverlayRegistrant.class)
- public abstract SystemUI bindDreamOverlayRegistrant(
+ public abstract CoreStartable bindDreamOverlayRegistrant(
DreamOverlayRegistrant dreamOverlayRegistrant);
/** Inject into AppWidgetOverlayPrimer. */
@Binds
@IntoMap
@ClassKey(AppWidgetOverlayPrimer.class)
- public abstract SystemUI bindAppWidgetOverlayPrimer(
+ public abstract CoreStartable bindAppWidgetOverlayPrimer(
AppWidgetOverlayPrimer appWidgetOverlayPrimer);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 20c46da14e63..994c63002ac2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -30,8 +30,8 @@ import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.util.Log;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -40,7 +40,7 @@ import javax.inject.Inject;
* {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
* the designated dream overlay component.
*/
-public class DreamOverlayRegistrant extends SystemUI {
+public class DreamOverlayRegistrant extends CoreStartable {
private static final String TAG = "DreamOverlayRegistrant";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final IDreamManager mDreamManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
index a0c7c29e0191..563f70776209 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
@@ -23,8 +23,8 @@ import android.view.Gravity;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.OverlayHostView;
@@ -36,7 +36,7 @@ import javax.inject.Inject;
* {@link AppWidgetOverlayPrimer} reads the configured App Widget Overlay from resources on start
* and populates them into the {@link DreamOverlayStateController}.
*/
-public class AppWidgetOverlayPrimer extends SystemUI {
+public class AppWidgetOverlayPrimer extends CoreStartable {
private final Resources mResources;
private final DreamOverlayStateController mDreamOverlayStateController;
private final AppWidgetOverlayComponent.Factory mComponentFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 6880674fd8f0..27e254f176fd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -17,22 +17,11 @@
package com.android.systemui.flags;
import android.content.Context;
-import android.content.res.Resources;
import android.util.FeatureFlagUtils;
import android.util.Log;
-import android.util.SparseArray;
import android.widget.Toast;
-import androidx.annotation.BoolRes;
-
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import javax.inject.Inject;
@@ -43,31 +32,13 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class FeatureFlags {
- private final Resources mResources;
private final FlagReader mFlagReader;
private final Context mContext;
- private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
- private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
- private final SparseArray<Boolean> mCachedFlags = new SparseArray<>();
@Inject
- public FeatureFlags(@Main Resources resources, FlagReader flagReader, Context context) {
- mResources = resources;
+ public FeatureFlags(FlagReader flagReader, Context context) {
mFlagReader = flagReader;
mContext = context;
-
- flagReader.addListener(mListener);
- }
-
- private final FlagReader.Listener mListener = id -> {
- if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
- mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
- }
- };
-
- @VisibleForTesting
- void addFlag(Flag<?> flag) {
- mFlagMap.put(flag.getId(), flag);
}
/**
@@ -75,32 +46,7 @@ public class FeatureFlags {
* @return The value of the flag.
*/
public boolean isEnabled(BooleanFlag flag) {
- boolean def = flag.getDefault();
- if (flag.hasResourceOverride()) {
- try {
- def = isEnabledInOverlay(flag.getResourceOverride());
- } catch (Resources.NotFoundException e) {
- // no-op
- }
- }
- return mFlagReader.isEnabled(flag.getId(), def);
- }
-
- /**
- * @param flag The {@link IntFlag} of interest.
-
- /** Add a listener for a specific flag. */
- public void addFlagListener(Flag<?> flag, Listener listener) {
- mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
- mListeners.get(flag.getId()).add(listener);
- mFlagMap.putIfAbsent(flag.getId(), flag);
- }
-
- /** Remove a listener for a specific flag. */
- public void removeFlagListener(Flag<?> flag, Listener listener) {
- if (mListeners.containsKey(flag.getId())) {
- mListeners.get(flag.getId()).remove(listener);
- }
+ return mFlagReader.isEnabled(flag);
}
public void assertLegacyPipelineEnabled() {
@@ -209,20 +155,4 @@ public class FeatureFlags {
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
-
- private boolean isEnabledInOverlay(@BoolRes int resId) {
- synchronized (mCachedFlags) {
- if (!mCachedFlags.contains(resId)) {
- mCachedFlags.put(resId, mResources.getBoolean(resId));
- }
-
- return mCachedFlags.get(resId);
- }
- }
-
- /** Simple interface for beinga alerted when a specific flag changes value. */
- public interface Listener {
- /** */
- void onFlagChanged(Flag<?> flag);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 86c8565bf8ef..e746cafb5ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -19,7 +19,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -36,7 +36,8 @@ import javax.inject.Provider;
* Manages power menu plugins and communicates power menu actions to the StatusBar.
*/
@SysUISingleton
-public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
+public class GlobalActionsComponent extends CoreStartable
+ implements Callbacks, GlobalActionsManager {
private final CommandQueue mCommandQueue;
private final ExtensionController mExtensionController;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 42f455af7df4..1c0b104b6945 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -49,9 +49,9 @@ import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -60,7 +60,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Set;
-public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
+public class KeyboardUI extends CoreStartable implements InputManager.OnTabletModeChangedListener {
private static final String TAG = "KeyboardUI";
private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 2cc564bf8452..a6455e625afb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -30,8 +30,8 @@ import com.android.keyguard.KeyguardClockSwitchController
import com.android.keyguard.KeyguardViewController
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -88,7 +88,8 @@ const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.4f
class KeyguardUnlockAnimationController @Inject constructor(
context: Context,
private val keyguardStateController: KeyguardStateController,
- private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
+ private val
+ keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController,
private val featureFlags: FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7bb7cc477693..e97e7622c272 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -99,8 +99,8 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -176,7 +176,7 @@ import dagger.Lazy;
* directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
* thread of the keyguard.
*/
-public class KeyguardViewMediator extends SystemUI implements Dumpable,
+public class KeyguardViewMediator extends CoreStartable implements Dumpable,
StatusBarStateController.StateListener {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 553b6d8679db..ae5f9b63fb3d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -37,7 +37,7 @@ import android.os.UserHandle;
import android.provider.MediaStore;
import android.util.Log;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -48,7 +48,7 @@ import java.util.HashMap;
* Service that offers to play ringtones by {@link Uri}, since our process has
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
*/
-public class RingtonePlayer extends SystemUI {
+public class RingtonePlayer extends CoreStartable {
private static final String TAG = "RingtonePlayer";
private static final boolean LOGD = false;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 42dd8862576b..19812697719c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -79,6 +79,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private final NotificationEntryManager mNotificationEntryManager;
@VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -111,6 +112,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mUiEventLogger = uiEventLogger;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
void start(@NonNull Callback cb) {
@@ -477,7 +480,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
}
boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
- return !isActiveRemoteDevice(device);
+ // TODO(b/202500642): Also enable volume control for remote non-group sessions.
+ return !isActiveRemoteDevice(device)
+ || mVolumeAdjustmentForRemoteGroupSessions;
}
private final MediaController.Callback mCb = new MediaController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
index 31e49390b9b8..d60172a17988 100644
--- a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
@@ -25,8 +25,8 @@ import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.util.Slog;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -40,7 +40,7 @@ import javax.inject.Inject;
* documented at {@link #handleTaskStackChanged} apply.
*/
@SysUISingleton
-public class HomeSoundEffectController extends SystemUI {
+public class HomeSoundEffectController extends CoreStartable {
private static final String TAG = "HomeSoundEffectController";
private final AudioManager mAudioManager;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index c351d13e9b16..0e6e8a433799 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -24,9 +24,6 @@ import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
@@ -613,8 +610,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
- updateAccessibilityButtonModeIfNeeded();
-
return barView;
}
@@ -1405,34 +1400,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
updateSystemUiStateFlags(a11yFlags);
}
- private void updateAccessibilityButtonModeIfNeeded() {
- final int mode = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
- // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
- // mode, so we don't need to update it.
- if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return;
- }
-
- // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
- if (QuickStepContract.isGesturalMode(mNavBarMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
- UserHandle.USER_CURRENT);
- // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
- } else if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
- }
- }
-
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
@@ -1550,6 +1517,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ // update assistant entry points on system navigation radio button click
+ updateAssistantEntrypoints();
+
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
if (getBarTransitions() != null) {
@@ -1557,7 +1527,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
updateScreenPinningGestures();
- updateAccessibilityButtonModeIfNeeded();
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4959c7d6808d..3dc79c43d894 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -16,10 +16,14 @@
package com.android.systemui.navigationbar;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -27,6 +31,8 @@ import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -46,6 +52,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -142,6 +149,8 @@ public class NavigationBarController implements
}
final int oldMode = mNavMode;
mNavMode = mode;
+ updateAccessibilityButtonModeIfNeeded();
+
mHandler.post(() -> {
// create/destroy nav bar based on nav mode only in unfolded state
if (oldMode != mNavMode) {
@@ -157,6 +166,35 @@ public class NavigationBarController implements
});
}
+ private void updateAccessibilityButtonModeIfNeeded() {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final int mode = Settings.Secure.getIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+ // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+ // mode, so we don't need to update it.
+ if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return;
+ }
+
+ // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+ if (QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+ Settings.Secure.putIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+ UserHandle.USER_CURRENT);
+ // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+ } else if (!QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+ Settings.Secure.putIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ }
+ }
+
/** @see #initializeTaskbarIfNecessary() */
private boolean updateNavbarForTaskbar() {
boolean taskbarShown = initializeTaskbarIfNecessary();
@@ -222,6 +260,8 @@ public class NavigationBarController implements
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
+ updateAccessibilityButtonModeIfNeeded();
+
// Don't need to create nav bar on the default display if we initialize TaskBar.
final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
&& !initializeTaskbarIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 4e1e1cd23c14..b483e59b4716 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -58,9 +58,9 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -247,7 +247,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
.setContentText(mContext.getString(R.string.invalid_charger_text))
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, UserHandle.ALL);
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, n, UserHandle.ALL);
@@ -298,7 +298,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
nb.setOnlyAlertOnce(!mPlaySound);
mPlaySound = false;
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
@@ -320,7 +320,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mContext.getString(R.string.no_auto_saver_action),
pendingBroadcast(ACTION_AUTO_SAVER_NO_THANKS));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.notifyAsUser(
@@ -397,7 +397,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
.setColor(Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorError));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
}
@@ -484,7 +484,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
pendingBroadcast(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING))
.setColor(Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorError));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.notifyAsUser(
TAG_TEMPERATURE, SystemMessage.NOTE_THERMAL_SHUTDOWN, n, UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 625265485f6d..37a0f5949e16 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -42,9 +42,9 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -62,7 +62,7 @@ import javax.inject.Inject;
import dagger.Lazy;
@SysUISingleton
-public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
+public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
static final String TAG = "PowerUI";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 875097625a6f..5510eb172cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -45,8 +45,8 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.privacy.PrivacyChipBuilder;
import com.android.systemui.privacy.PrivacyItem;
@@ -67,7 +67,7 @@ import javax.inject.Inject;
* recording audio, accessing the camera or accessing the location.
*/
@SysUISingleton
-public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback,
+public class TvOngoingPrivacyChip extends CoreStartable implements PrivacyItemController.Callback,
PrivacyChipDrawable.PrivacyChipDrawableListener {
private static final String TAG = "TvOngoingPrivacyChip";
private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 6f6dd9cb22c7..b7a44a43f4b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,7 +21,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.provider.Settings;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
@@ -30,7 +30,7 @@ import java.io.PrintWriter;
/**
* A proxy to a Recents implementation.
*/
-public class Recents extends SystemUI implements CommandQueue.Callbacks {
+public class Recents extends CoreStartable implements CommandQueue.Callbacks {
private final RecentsImplementation mImpl;
private final CommandQueue mCommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 58a54f6ce0ed..28bdd5358956 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -30,8 +30,8 @@ import android.util.DisplayMetrics;
import android.view.WindowManager;
import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.util.NotificationChannels;
import javax.inject.Inject;
@@ -86,7 +86,7 @@ public class ScreenshotNotificationsController {
b.setContentIntent(pendingIntent);
}
- SystemUI.overrideNotificationAppName(mContext, b, true);
+ CoreStartable.overrideNotificationAppName(mContext, b, true);
Notification n = new Notification.BigTextStyle(b)
.bigText(errorMsg)
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 993343808ee9..10aa12bc97ac 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -24,7 +24,7 @@ import android.view.KeyEvent;
import android.view.WindowManagerGlobal;
import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.wm.shell.legacysplitscreen.DividerView;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -37,7 +37,7 @@ import javax.inject.Inject;
* Dispatches shortcut to System UI components
*/
@SysUISingleton
-public class ShortcutKeyDispatcher extends SystemUI
+public class ShortcutKeyDispatcher extends CoreStartable
implements ShortcutKeyServiceProxy.Callbacks {
private static final String TAG = "ShortcutKeyDispatcher";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 74ebfe5ad5e4..1c0088709f14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -818,7 +818,7 @@ public class KeyguardIndicationController {
}
private void showTryFingerprintMsg(int msgId, String a11yString) {
- if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+ if (mKeyguardUpdateMonitor.isUdfpsSupported()) {
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 464b2b69c58e..ff3e97af72a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -35,7 +35,7 @@ import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index cbb3aba5cc64..da2b85ee0b61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -173,7 +173,7 @@ public class StatusBarStateControllerImpl implements
}
// Record the to-be mState and mLastState
- recordHistoricalState(state, mState);
+ recordHistoricalState(state /* newState */, mState /* lastState */, false);
// b/139259891
if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
@@ -206,6 +206,7 @@ public class StatusBarStateControllerImpl implements
@Override
public void setUpcomingState(int nextState) {
mUpcomingState = nextState;
+ recordHistoricalState(mUpcomingState /* newState */, mState /* lastState */, true);
}
@Override
@@ -505,31 +506,36 @@ public class StatusBarStateControllerImpl implements
}
}
- private void recordHistoricalState(int currentState, int lastState) {
+ private void recordHistoricalState(int newState, int lastState, boolean upcoming) {
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
HistoricalState state = mHistoricalRecords[mHistoryIndex];
- state.mState = currentState;
+ state.mNewState = newState;
state.mLastState = lastState;
state.mTimestamp = System.currentTimeMillis();
+ state.mUpcoming = upcoming;
}
/**
* For keeping track of our previous state to help with debugging
*/
private static class HistoricalState {
- int mState;
+ int mNewState;
int mLastState;
long mTimestamp;
+ boolean mUpcoming;
@Override
public String toString() {
if (mTimestamp != 0) {
StringBuilder sb = new StringBuilder();
- sb.append("state=").append(mState)
- .append(" (").append(describe(mState)).append(")");
- sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
+ if (mUpcoming) {
+ sb.append("upcoming-");
+ }
+ sb.append("newState=").append(mNewState)
+ .append("(").append(describe(mNewState)).append(")");
+ sb.append(" lastState=").append(mLastState).append("(").append(describe(mLastState))
.append(")");
- sb.append("timestamp=")
+ sb.append(" timestamp=")
.append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
return sb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index aa86daaae125..d5cba72f99d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -55,7 +55,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -73,8 +73,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.statusbar.window.StatusBarWindowModule;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index e58ea7b8b5f3..60d13170b6db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,9 +51,9 @@ import android.util.Pair;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.statusbar.CommandQueue;
@@ -71,7 +71,7 @@ import javax.inject.Inject;
* splitted screen.
*/
@SysUISingleton
-public class InstantAppNotifier extends SystemUI
+public class InstantAppNotifier extends CoreStartable
implements CommandQueue.Callbacks, KeyguardStateController.Callback {
private static final String TAG = "InstantAppNotifier";
public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 82f35a814d22..2437415d0c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -638,7 +638,7 @@ public class NotificationEntryManager implements
// Construct the expanded view.
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
+ mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback);
}
mPendingNotifications.put(key, entry);
@@ -695,7 +695,7 @@ public class NotificationEntryManager implements
}
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
+ mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback);
}
updateNotifications("updateNotificationInternal");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 8562a2e55a4f..4f3c287d5f1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.notification.collection;
-import com.android.internal.statusbar.IStatusBarService;
+import androidx.annotation.NonNull;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
@@ -34,23 +35,13 @@ import javax.inject.Inject;
@SysUISingleton
public class NotifInflaterImpl implements NotifInflater {
- private final IStatusBarService mStatusBarService;
- private final NotifCollection mNotifCollection;
private final NotifInflationErrorManager mNotifErrorManager;
- private final NotifPipeline mNotifPipeline;
private NotificationRowBinderImpl mNotificationRowBinder;
@Inject
- public NotifInflaterImpl(
- IStatusBarService statusBarService,
- NotifCollection notifCollection,
- NotifInflationErrorManager errorManager,
- NotifPipeline notifPipeline) {
- mStatusBarService = statusBarService;
- mNotifCollection = notifCollection;
+ public NotifInflaterImpl(NotifInflationErrorManager errorManager) {
mNotifErrorManager = errorManager;
- mNotifPipeline = notifPipeline;
}
/**
@@ -61,8 +52,9 @@ public class NotifInflaterImpl implements NotifInflater {
}
@Override
- public void rebindViews(NotificationEntry entry, InflationCallback callback) {
- inflateViews(entry, callback);
+ public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
+ inflateViews(entry, params, callback);
}
/**
@@ -70,10 +62,12 @@ public class NotifInflaterImpl implements NotifInflater {
* views are bound.
*/
@Override
- public void inflateViews(NotificationEntry entry, InflationCallback callback) {
+ public void inflateViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
try {
requireBinder().inflateViews(
entry,
+ params,
wrapInflationCallback(callback));
} catch (InflationException e) {
mNotifErrorManager.setInflationError(entry, e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 50d7324df2b4..9ae9fe508944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -58,12 +58,13 @@ import javax.inject.Inject
* appropriately).
* 3. OnBeforeTransformGroupListeners are fired ([.addOnBeforeTransformGroupsListener])
* 4. NotifPromoters are called on each notification with a parent ([.addPromoter])
- * 5. Finalize filters are fired on each notification ([.addFinalizeFilter])
- * 6. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
- * 7. Top-level entries are assigned sections by NotifSections ([.setSections])
- * 8. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
- * 9. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
- * 10. The list is handed off to the view layer to be rendered
+ * 5. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
+ * 6. Top-level entries are assigned sections by NotifSections ([.setSections])
+ * 7. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
+ * 8. OnBeforeFinalizeFilterListeners are fired ([.addOnBeforeFinalizeFilterListener])
+ * 9. Finalize filters are fired on each notification ([.addFinalizeFilter])
+ * 10. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
+ * 11. The list is handed off to the view layer to be rendered
*/
@SysUISingleton
class NotifPipeline @Inject constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 15872da14416..72cd95128779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -28,12 +28,15 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.os.Trace;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -64,6 +67,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -363,22 +367,24 @@ public class ShadeListBuilder implements Dumpable {
mPipelineState.incrementTo(STATE_GROUP_STABILIZING);
stabilizeGroupingNotifs(mNotifList);
+ // Step 5: Section & Sort
+ // Assign each top-level entry a section, and copy to all of its children
+ dispatchOnBeforeSort(mReadOnlyNotifList);
+ mPipelineState.incrementTo(STATE_SORTING);
+ assignSections();
+ notifySectionEntriesUpdated();
+ // Sort the list by section and then within section by our list of custom comparators
+ sortListAndGroups();
- // Step 5: Filter out entries after pre-group filtering, grouping and promoting
- // Now filters can see grouping information to determine whether to filter or not.
+ // Step 6: Filter out entries after pre-group filtering, grouping, promoting, and sorting
+ // Now filters can see grouping, sectioning, and order information to determine whether
+ // to filter or not.
dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
filterNotifs(mNotifList, mNewNotifList, mNotifFinalizeFilters);
applyNewNotifList();
pruneIncompleteGroups(mNotifList);
- // Step 6: Sort
- // Assign each top-level entry a section, then sort the list by section and then within
- // section by our list of custom comparators
- dispatchOnBeforeSort(mReadOnlyNotifList);
- mPipelineState.incrementTo(STATE_SORTING);
- sortListAndNotifySections();
-
// Step 7: Lock in our group structure and log anything that's changed since the last run
mPipelineState.incrementTo(STATE_FINALIZING);
logChanges();
@@ -408,18 +414,15 @@ public class ShadeListBuilder implements Dumpable {
private void notifySectionEntriesUpdated() {
Trace.beginSection("ShadeListBuilder.notifySectionEntriesUpdated");
- NotifSection currentSection = null;
mTempSectionMembers.clear();
- for (int i = 0; i < mNotifList.size(); i++) {
- ListEntry currentEntry = mNotifList.get(i);
- if (currentSection != currentEntry.getSection()) {
- if (currentSection != null) {
- currentSection.getSectioner().onEntriesUpdated(mTempSectionMembers);
- mTempSectionMembers.clear();
+ for (NotifSection section : mNotifSections) {
+ for (ListEntry entry : mNotifList) {
+ if (section == entry.getSection()) {
+ mTempSectionMembers.add(entry);
}
- currentSection = currentEntry.getSection();
}
- mTempSectionMembers.add(currentEntry);
+ section.getSectioner().onEntriesUpdated(mTempSectionMembers);
+ mTempSectionMembers.clear();
}
Trace.endSection();
}
@@ -653,16 +656,15 @@ public class ShadeListBuilder implements Dumpable {
final List<NotificationEntry> children = group.getRawChildren();
if (group.getSummary() != null && children.size() == 0) {
- shadeList.remove(i);
- i--;
-
NotificationEntry summary = group.getSummary();
summary.setParent(ROOT_ENTRY);
- shadeList.add(summary);
+ // The list may be sorted; replace the group with the summary, in its place
+ shadeList.set(i, summary);
group.setSummary(null);
annulAddition(group, shadeList);
+ i--; // The node we visited is gone, so be sure to visit this index again.
} else if (group.getSummary() == null
|| children.size() < MIN_CHILDREN_FOR_GROUP) {
@@ -680,7 +682,6 @@ public class ShadeListBuilder implements Dumpable {
// its children (if any) directly to top-level.
shadeList.remove(i);
- i--;
if (group.getSummary() != null) {
final NotificationEntry summary = group.getSummary();
@@ -691,11 +692,14 @@ public class ShadeListBuilder implements Dumpable {
for (int j = 0; j < children.size(); j++) {
final NotificationEntry child = children.get(j);
child.setParent(ROOT_ENTRY);
- shadeList.add(child);
+ // The list may be sorted, so add the children in order where the group was.
+ shadeList.add(i + j, child);
}
children.clear();
annulAddition(group, shadeList);
+
+ i--; // The node we visited is gone, so be sure to visit this index again.
}
}
}
@@ -765,9 +769,9 @@ public class ShadeListBuilder implements Dumpable {
}
}
- private void sortListAndNotifySections() {
- Trace.beginSection("ShadeListBuilder.sortListAndNotifySections");
- // Assign sections to top-level elements and sort their children
+ private void assignSections() {
+ Trace.beginSection("ShadeListBuilder.assignSections");
+ // Assign sections to top-level elements and their children
for (ListEntry entry : mNotifList) {
NotifSection section = applySections(entry);
if (entry instanceof GroupEntry) {
@@ -775,27 +779,64 @@ public class ShadeListBuilder implements Dumpable {
for (NotificationEntry child : parent.getChildren()) {
setEntrySection(child, section);
}
+ }
+ }
+ Trace.endSection();
+ }
+
+ private void sortListAndGroups() {
+ Trace.beginSection("ShadeListBuilder.sortListAndGroups");
+ // Assign sections to top-level elements and sort their children
+ for (ListEntry entry : mNotifList) {
+ if (entry instanceof GroupEntry) {
+ GroupEntry parent = (GroupEntry) entry;
parent.sortChildren(mGroupChildrenComparator);
}
}
mNotifList.sort(mTopLevelComparator);
assignIndexes(mNotifList);
- notifySectionEntriesUpdated();
+ // Check for suppressed order changes
+ if (!mNotifStabilityManager.isEveryChangeAllowed()) {
+ mForceReorderable = true;
+ boolean isSorted = isSorted(mNotifList, mTopLevelComparator);
+ mForceReorderable = false;
+ if (!isSorted) {
+ mNotifStabilityManager.onEntryReorderSuppressed();
+ }
+ }
Trace.endSection();
}
+ /** Determine whether the items in the list are sorted according to the comparator */
+ @VisibleForTesting
+ public static <T> boolean isSorted(List<T> items, Comparator<T> comparator) {
+ if (items.size() <= 1) {
+ return true;
+ }
+ Iterator<T> iterator = items.iterator();
+ T previous = iterator.next();
+ T current;
+ while (iterator.hasNext()) {
+ current = iterator.next();
+ if (comparator.compare(previous, current) > 0) {
+ return false;
+ }
+ previous = current;
+ }
+ return true;
+ }
+
/**
* Assign the index of each notification relative to the total order
- * @param notifList
*/
- private void assignIndexes(List<ListEntry> notifList) {
+ private static void assignIndexes(List<ListEntry> notifList) {
if (notifList.size() == 0) return;
- NotifSection currentSection = notifList.get(0).getSection();
+ NotifSection currentSection = requireNonNull(notifList.get(0).getSection());
int sectionMemberIndex = 0;
for (int i = 0; i < notifList.size(); i++) {
ListEntry entry = notifList.get(i);
- NotifSection section = entry.getSection();
+ NotifSection section = requireNonNull(entry.getSection());
if (section.getIndex() != currentSection.getIndex()) {
sectionMemberIndex = 0;
currentSection = section;
@@ -960,8 +1001,14 @@ public class ShadeListBuilder implements Dumpable {
return cmp;
};
+ /**
+ * A flag that is set to true when we want to run the comparators as if all reordering is
+ * allowed. This is used to check if the list is "out of order" after the sort is complete.
+ */
+ private boolean mForceReorderable = false;
+
private boolean canReorder(ListEntry entry) {
- return mNotifStabilityManager.isEntryReorderingAllowed(entry);
+ return mForceReorderable || mNotifStabilityManager.isEntryReorderingAllowed(entry);
}
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
@@ -1031,8 +1078,11 @@ public class ShadeListBuilder implements Dumpable {
private void setEntrySection(ListEntry entry, NotifSection finalSection) {
entry.getAttachState().setSection(finalSection);
NotificationEntry representativeEntry = entry.getRepresentativeEntry();
- if (representativeEntry != null && finalSection != null) {
- representativeEntry.setBucket(finalSection.getBucket());
+ if (representativeEntry != null) {
+ representativeEntry.getAttachState().setSection(finalSection);
+ if (finalSection != null) {
+ representativeEntry.setBucket(finalSection.getBucket());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index afdfb3bdeef6..644f248fca00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -26,6 +26,9 @@ import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
@@ -35,6 +38,8 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -46,7 +51,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import javax.inject.Inject;
@@ -66,14 +70,24 @@ public class PreparationCoordinator implements Coordinator {
private final NotifInflater mNotifInflater;
private final NotifInflationErrorManager mNotifErrorManager;
private final NotifViewBarn mViewBarn;
- private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
+ private final NotifUiAdjustmentProvider mAdjustmentProvider;
+ private final ArrayMap<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
+
+ /**
+ * The map of notifications to the NotifUiAdjustment (i.e. parameters) that were calculated
+ * when the inflation started. If an update of any kind results in the adjustment changing,
+ * then the row must be reinflated. If the row is being inflated, then the inflation must be
+ * aborted and restarted.
+ */
+ private final ArrayMap<NotificationEntry, NotifUiAdjustment> mInflationAdjustments =
+ new ArrayMap<>();
/**
* The set of notifications that are currently inflating something. Note that this is
* separate from inflation state as a view could either be uninflated or inflated and still be
* inflating something.
*/
- private final Set<NotificationEntry> mInflatingNotifs = new ArraySet<>();
+ private final ArraySet<NotificationEntry> mInflatingNotifs = new ArraySet<>();
private final IStatusBarService mStatusBarService;
@@ -92,12 +106,14 @@ public class PreparationCoordinator implements Coordinator {
NotifInflater notifInflater,
NotifInflationErrorManager errorManager,
NotifViewBarn viewBarn,
+ NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service) {
this(
logger,
notifInflater,
errorManager,
viewBarn,
+ adjustmentProvider,
service,
CHILD_BIND_CUTOFF,
MAX_GROUP_INFLATION_DELAY);
@@ -109,6 +125,7 @@ public class PreparationCoordinator implements Coordinator {
NotifInflater notifInflater,
NotifInflationErrorManager errorManager,
NotifViewBarn viewBarn,
+ NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service,
int childBindCutoff,
long maxGroupInflationDelay) {
@@ -116,6 +133,7 @@ public class PreparationCoordinator implements Coordinator {
mNotifInflater = notifInflater;
mNotifErrorManager = errorManager;
mViewBarn = viewBarn;
+ mAdjustmentProvider = adjustmentProvider;
mStatusBarService = service;
mChildBindCutoff = childBindCutoff;
mMaxGroupInflationDelay = maxGroupInflationDelay;
@@ -160,6 +178,7 @@ public class PreparationCoordinator implements Coordinator {
public void onEntryCleanUp(NotificationEntry entry) {
mInflationStates.remove(entry);
mViewBarn.removeViewForEntry(entry);
+ mInflationAdjustments.remove(entry);
}
};
@@ -269,39 +288,78 @@ public class PreparationCoordinator implements Coordinator {
}
private void inflateRequiredNotifViews(NotificationEntry entry) {
+ NotifUiAdjustment newAdjustment = mAdjustmentProvider.calculateAdjustment(entry);
if (mInflatingNotifs.contains(entry)) {
// Already inflating this entry
+ String errorIfNoOldAdjustment = "Inflating notification has no adjustments";
+ if (needToReinflate(entry, newAdjustment, errorIfNoOldAdjustment)) {
+ inflateEntry(entry, newAdjustment, "adjustment changed while inflating");
+ }
return;
}
@InflationState int state = mInflationStates.get(entry);
switch (state) {
case STATE_UNINFLATED:
- inflateEntry(entry, "entryAdded");
+ inflateEntry(entry, newAdjustment, "entryAdded");
break;
case STATE_INFLATED_INVALID:
- rebind(entry, "entryUpdated");
+ rebind(entry, newAdjustment, "entryUpdated");
break;
case STATE_INFLATED:
+ String errorIfNoOldAdjustment = "Fully inflated notification has no adjustments";
+ if (needToReinflate(entry, newAdjustment, errorIfNoOldAdjustment)) {
+ rebind(entry, newAdjustment, "adjustment changed after inflated");
+ }
+ break;
case STATE_ERROR:
+ if (needToReinflate(entry, newAdjustment, null)) {
+ inflateEntry(entry, newAdjustment, "adjustment changed after error");
+ }
+ break;
default:
// Nothing to do.
}
}
- private void inflateEntry(NotificationEntry entry, String reason) {
+ private boolean needToReinflate(@NonNull NotificationEntry entry,
+ @NonNull NotifUiAdjustment newAdjustment, @Nullable String oldAdjustmentMissingError) {
+ NotifUiAdjustment oldAdjustment = mInflationAdjustments.get(entry);
+ if (oldAdjustment == null) {
+ if (oldAdjustmentMissingError == null) {
+ return true;
+ } else {
+ throw new IllegalStateException(oldAdjustmentMissingError);
+ }
+ }
+ return NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment);
+ }
+
+ private void inflateEntry(NotificationEntry entry,
+ NotifUiAdjustment newAdjustment,
+ String reason) {
abortInflation(entry, reason);
+ mInflationAdjustments.put(entry, newAdjustment);
mInflatingNotifs.add(entry);
- mNotifInflater.inflateViews(entry, this::onInflationFinished);
+ NotifInflater.Params params = getInflaterParams(newAdjustment, reason);
+ mNotifInflater.inflateViews(entry, params, this::onInflationFinished);
}
- private void rebind(NotificationEntry entry, String reason) {
+ private void rebind(NotificationEntry entry,
+ NotifUiAdjustment newAdjustment,
+ String reason) {
+ mInflationAdjustments.put(entry, newAdjustment);
mInflatingNotifs.add(entry);
- mNotifInflater.rebindViews(entry, this::onInflationFinished);
+ NotifInflater.Params params = getInflaterParams(newAdjustment, reason);
+ mNotifInflater.rebindViews(entry, params, this::onInflationFinished);
+ }
+
+ NotifInflater.Params getInflaterParams(NotifUiAdjustment adjustment, String reason) {
+ return new NotifInflater.Params(adjustment.isMinimized(), reason);
}
private void abortInflation(NotificationEntry entry, String reason) {
mLogger.logInflationAborted(entry.getKey(), reason);
- entry.abortTask();
+ mNotifInflater.abortInflation(entry);
mInflatingNotifs.remove(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 79eb0898bc0b..c60ebcdc5fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -19,12 +19,12 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
+import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
@@ -50,6 +51,7 @@ public class RankingCoordinator implements Coordinator {
public static final boolean SHOW_ALL_SECTIONS = false;
private final StatusBarStateController mStatusBarStateController;
private final HighPriorityProvider mHighPriorityProvider;
+ private final NotifUiAdjustmentProvider mAdjustmentProvider;
private final NodeController mSilentNodeController;
private final SectionHeaderController mSilentHeaderController;
private final NodeController mAlertingHeaderController;
@@ -60,11 +62,13 @@ public class RankingCoordinator implements Coordinator {
public RankingCoordinator(
StatusBarStateController statusBarStateController,
HighPriorityProvider highPriorityProvider,
+ NotifUiAdjustmentProvider adjustmentProvider,
@AlertingHeader NodeController alertingHeaderController,
@SilentHeader SectionHeaderController silentHeaderController,
@SilentHeader NodeController silentNodeController) {
mStatusBarStateController = statusBarStateController;
mHighPriorityProvider = highPriorityProvider;
+ mAdjustmentProvider = adjustmentProvider;
mAlertingHeaderController = alertingHeaderController;
mSilentNodeController = silentNodeController;
mSilentHeaderController = silentHeaderController;
@@ -73,10 +77,10 @@ public class RankingCoordinator implements Coordinator {
@Override
public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
+ mAdjustmentProvider.setLowPrioritySections(Collections.singleton(mMinimizedNotifSectioner));
pipeline.addPreGroupFilter(mSuspendedFilter);
pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
- pipeline.addOnBeforeSortListener(entries -> resetClearAllFlags());
}
public NotifSectioner getAlertingSectioner() {
@@ -126,6 +130,7 @@ public class RankingCoordinator implements Coordinator {
@Nullable
@Override
public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ mHasSilentEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
mHasSilentEntries = true;
@@ -154,6 +159,7 @@ public class RankingCoordinator implements Coordinator {
@Nullable
@Override
public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ mHasMinimizedEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
mHasMinimizedEntries = true;
@@ -189,12 +195,6 @@ public class RankingCoordinator implements Coordinator {
}
};
- @VisibleForTesting
- protected void resetClearAllFlags() {
- mHasSilentEntries = false;
- mHasMinimizedEntries = false;
- }
-
private final StatusBarStateController.StateListener mStatusBarStateCallback =
new StatusBarStateController.StateListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 32b1cf6bfdca..75489b1faadb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -19,11 +19,12 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import android.annotation.NonNull;
-
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
@@ -34,6 +35,8 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -53,7 +56,7 @@ import javax.inject.Inject;
*/
// TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton
-public class VisualStabilityCoordinator implements Coordinator {
+public class VisualStabilityCoordinator implements Coordinator, Dumpable {
private final DelayableExecutor mDelayableExecutor;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final StatusBarStateController mStatusBarStateController;
@@ -66,6 +69,7 @@ public class VisualStabilityCoordinator implements Coordinator {
private boolean mReorderingAllowed;
private boolean mIsSuppressingGroupChange = false;
private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>();
+ private boolean mIsSuppressingEntryReorder = false;
// key: notification key that can temporarily change its section
// value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable
@@ -77,6 +81,7 @@ public class VisualStabilityCoordinator implements Coordinator {
@Inject
public VisualStabilityCoordinator(
+ DumpManager dumpManager,
HeadsUpManager headsUpManager,
WakefulnessLifecycle wakefulnessLifecycle,
StatusBarStateController statusBarStateController,
@@ -86,6 +91,8 @@ public class VisualStabilityCoordinator implements Coordinator {
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDelayableExecutor = delayableExecutor;
+
+ dumpManager.registerDumpable(this);
}
@Override
@@ -99,7 +106,6 @@ public class VisualStabilityCoordinator implements Coordinator {
pipeline.setVisualStabilityManager(mNotifStabilityManager);
}
- // TODO(b/203828145): Ensure stability manager handles minimized state changes
// TODO(b/203826051): Ensure stability manager can allow reordering off-screen
// HUNs to the top of the shade
private final NotifStabilityManager mNotifStabilityManager =
@@ -108,6 +114,7 @@ public class VisualStabilityCoordinator implements Coordinator {
public void onBeginRun() {
mIsSuppressingGroupChange = false;
mEntriesWithSuppressedSectionChange.clear();
+ mIsSuppressingEntryReorder = false;
}
@Override
@@ -124,7 +131,7 @@ public class VisualStabilityCoordinator implements Coordinator {
mReorderingAllowed
|| mHeadsUpManager.isAlerting(entry.getKey())
|| mEntriesThatCanChangeSection.containsKey(entry.getKey());
- if (isSectionChangeAllowedForEntry) {
+ if (!isSectionChangeAllowedForEntry) {
mEntriesWithSuppressedSectionChange.add(entry.getKey());
}
return isSectionChangeAllowedForEntry;
@@ -134,11 +141,22 @@ public class VisualStabilityCoordinator implements Coordinator {
public boolean isEntryReorderingAllowed(ListEntry section) {
return mReorderingAllowed;
}
+
+ @Override
+ public boolean isEveryChangeAllowed() {
+ return mReorderingAllowed;
+ }
+
+ @Override
+ public void onEntryReorderSuppressed() {
+ mIsSuppressingEntryReorder = true;
+ }
};
private void updateAllowedStates() {
mReorderingAllowed = isReorderingAllowed();
- if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange())) {
+ if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange()
+ || mIsSuppressingEntryReorder)) {
mNotifStabilityManager.invalidateList();
}
}
@@ -211,4 +229,23 @@ public class VisualStabilityCoordinator implements Coordinator {
updateAllowedStates();
}
};
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("reorderingAllowed: " + mReorderingAllowed);
+ pw.println(" screenOn: " + mScreenOn);
+ pw.println(" panelExpanded: " + mPanelExpanded);
+ pw.println(" pulsing: " + mPulsing);
+ pw.println("isSuppressingGroupChange: " + mIsSuppressingGroupChange);
+ pw.println("isSuppressingEntryReorder: " + mIsSuppressingEntryReorder);
+ pw.println("entriesWithSuppressedSectionChange: "
+ + mEntriesWithSuppressedSectionChange.size());
+ for (String key : mEntriesWithSuppressedSectionChange) {
+ pw.println(" " + key);
+ }
+ pw.println("entriesThatCanChangeSection: " + mEntriesThatCanChangeSection.size());
+ for (String key : mEntriesThatCanChangeSection.keySet()) {
+ pw.println(" " + key);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index e3d76113d537..c59f18436b74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.inflation;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
- * Used by the {@link PreparationCoordinator}. When notifications are added or updated, the
+ * Used by the [PreparationCoordinator]. When notifications are added or updated, the
* NotifInflater is asked to (re)inflated and prepare their views. This inflation occurs off the
* main thread. When the inflation is finished, NotifInflater will trigger its InflationCallback.
*/
-public interface NotifInflater {
+interface NotifInflater {
/**
* Called to rebind the entry's views.
*
* @param callback callback called after inflation finishes
*/
- void rebindViews(NotificationEntry entry, InflationCallback callback);
+ fun rebindViews(entry: NotificationEntry, params: Params, callback: InflationCallback)
/**
* Called to inflate the views of an entry. Views are not considered inflated until all of its
@@ -37,18 +37,23 @@ public interface NotifInflater {
*
* @param callback callback called after inflation finishes
*/
- void inflateViews(NotificationEntry entry, InflationCallback callback);
+ fun inflateViews(entry: NotificationEntry, params: Params, callback: InflationCallback)
/**
* Request to stop the inflation of an entry. For example, called when a notification is
* removed and no longer needs to be inflated.
*/
- void abortInflation(NotificationEntry entry);
+ fun abortInflation(entry: NotificationEntry)
/**
* Callback once all the views are inflated and bound for a given NotificationEntry.
*/
interface InflationCallback {
- void onInflationFinished(NotificationEntry entry);
+ fun onInflationFinished(entry: NotificationEntry)
}
-}
+
+ /**
+ * A class holding parameters used when inflating the notification row
+ */
+ class Params(val isLowPriority: Boolean, val reason: String)
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
new file mode 100644
index 000000000000..9d86b7831f18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import android.app.Notification
+import android.app.RemoteInput
+import android.graphics.drawable.Icon
+import android.text.TextUtils
+
+/**
+ * An immutable object which contains minimal state extracted from an entry that represents state
+ * which can change without a direct app update (e.g. with a ranking update).
+ * Diffing two entries determines if view re-inflation is needed.
+ */
+class NotifUiAdjustment internal constructor(
+ val key: String,
+ val smartActions: List<Notification.Action>,
+ val smartReplies: List<CharSequence>,
+ val isConversation: Boolean,
+ val isMinimized: Boolean
+) {
+ companion object {
+ @JvmStatic
+ fun needReinflate(
+ oldAdjustment: NotifUiAdjustment,
+ newAdjustment: NotifUiAdjustment
+ ): Boolean = when {
+ oldAdjustment === newAdjustment -> false
+ oldAdjustment.isConversation != newAdjustment.isConversation -> true
+ oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
+ areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
+ newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
+ else -> false
+ }
+
+ private fun areDifferent(
+ first: List<Notification.Action>,
+ second: List<Notification.Action>
+ ): Boolean = when {
+ first === second -> false
+ first.size != second.size -> true
+ else -> first.asSequence().zip(second.asSequence()).any {
+ (!TextUtils.equals(it.first.title, it.second.title)) ||
+ (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
+ (it.first.actionIntent != it.second.actionIntent) ||
+ (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+ }
+ }
+
+ private fun areDifferent(first: Icon?, second: Icon?): Boolean = when {
+ first === second -> false
+ first == null || second == null -> true
+ else -> !first.sameAs(second)
+ }
+
+ private fun areDifferent(
+ first: Array<RemoteInput>?,
+ second: Array<RemoteInput>?
+ ): Boolean = when {
+ first === second -> false
+ first == null || second == null -> true
+ first.size != second.size -> true
+ else -> first.asSequence().zip(second.asSequence()).any {
+ (!TextUtils.equals(it.first.label, it.second.label)) ||
+ (areDifferent(it.first.choices, it.second.choices))
+ }
+ }
+
+ private fun areDifferent(
+ first: Array<CharSequence>?,
+ second: Array<CharSequence>?
+ ): Boolean = when {
+ first === second -> false
+ first == null || second == null -> true
+ first.size != second.size -> true
+ else -> first.asSequence().zip(second.asSequence()).any {
+ !TextUtils.equals(it.first, it.second)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
new file mode 100644
index 000000000000..3290cdffdceb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import javax.inject.Inject
+
+/**
+ * A class which provides an adjustment object to the preparation coordinator which is uses
+ * to ensure that notifications are reinflated when ranking-derived information changes.
+ */
+@SysUISingleton
+open class NotifUiAdjustmentProvider @Inject constructor() {
+
+ private lateinit var lowPrioritySections: Set<NotifSectioner>
+
+ /**
+ * Feed the provider the information it needs about which sections should have minimized top
+ * level views, so that it can calculate the correct minimized value in the adjustment.
+ */
+ fun setLowPrioritySections(sections: Collection<NotifSectioner>) {
+ lowPrioritySections = sections.toSet()
+ }
+
+ private fun isEntryMinimized(entry: NotificationEntry): Boolean {
+ val section = entry.section ?: error("Entry must have a section to determine if minimized")
+ val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
+ val isLowPrioritySection = lowPrioritySections.contains(section.sectioner)
+ val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
+ val isGroupSummary = parent.summary == entry
+ return isLowPrioritySection && (isTopLevelEntry || isGroupSummary)
+ }
+
+ /**
+ * Returns a adjustment object for the given entry. This can be compared to a previous instance
+ * from the same notification using [NotifUiAdjustment.needReinflate] to determine if it
+ * should be reinflated.
+ */
+ fun calculateAdjustment(entry: NotificationEntry) = NotifUiAdjustment(
+ key = entry.key,
+ smartActions = entry.ranking.smartActions,
+ smartReplies = entry.ranking.smartReplies,
+ isConversation = entry.ranking.isConversation,
+ isMinimized = isEntryMinimized(entry)
+ )
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 1215ade2abb1..3a4701c9ac76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -32,12 +32,10 @@ public interface NotificationRowBinder {
/**
* Called when a notification has been added or updated. The binder must asynchronously inflate
* and bind the views associated with the notification.
- *
- * TODO: The caller is notified when the inflation completes, but this is currently a very
- * roundabout business. Add an explicit completion/failure callback to this method.
*/
void inflateViews(
NotificationEntry entry,
+ NotifInflater.Params params,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e5425cfc8c93..5c8e8b244abb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -25,6 +25,7 @@ import android.view.ViewGroup;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -32,6 +33,7 @@ import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
@@ -53,6 +55,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private static final String TAG = "NotificationViewManager";
private final Context mContext;
+ private final FeatureFlags mFeatureFlags;
private final NotificationMessagingUtil mMessagingUtil;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -72,6 +75,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
@Inject
public NotificationRowBinderImpl(
Context context,
+ FeatureFlags featureFlags,
NotificationMessagingUtil notificationMessagingUtil,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
@@ -82,6 +86,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
IconManager iconManager,
LowPriorityInflationHelper lowPriorityInflationHelper) {
mContext = context;
+ mFeatureFlags = featureFlags;
mNotifBindPipeline = notifBindPipeline;
mRowContentBindStage = rowContentBindStage;
mMessagingUtil = notificationMessagingUtil;
@@ -116,8 +121,13 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
@Override
public void inflateViews(
NotificationEntry entry,
+ NotifInflater.Params params,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException {
+ if (params == null) {
+ // weak assert that the params should always be passed in the new pipeline
+ mFeatureFlags.checkLegacyPipelineEnabled();
+ }
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
if (entry.rowExists()) {
@@ -125,7 +135,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
ExpandableNotificationRow row = entry.getRow();
row.reset();
updateRow(entry, row);
- inflateContentViews(entry, row, callback);
+ inflateContentViews(entry, params, row, callback);
} else {
mIconManager.createIcons(entry);
mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
@@ -144,7 +154,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
entry.setRowController(rowController);
bindRow(entry, row);
updateRow(entry, row);
- inflateContentViews(entry, row, callback);
+ inflateContentViews(entry, params, row, callback);
});
}
}
@@ -177,12 +187,13 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
NotificationUiAdjustment oldAdjustment,
NotificationUiAdjustment newAdjustment,
NotificationRowContentBinder.InflationCallback callback) {
+ mFeatureFlags.checkLegacyPipelineEnabled();
if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
if (entry.rowExists()) {
ExpandableNotificationRow row = entry.getRow();
row.reset();
updateRow(entry, row);
- inflateContentViews(entry, row, callback);
+ inflateContentViews(entry, null, row, callback);
} else {
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
@@ -216,15 +227,24 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
*/
private void inflateContentViews(
NotificationEntry entry,
+ NotifInflater.Params inflaterParams,
ExpandableNotificationRow row,
@Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
final boolean useIncreasedCollapsedHeight =
mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
- // If this is our first time inflating, we don't actually know the groupings for real
- // yet, so we might actually inflate a low priority content view incorrectly here and have
- // to correct it later in the pipeline. On subsequent inflations (i.e. updates), this
- // should inflate the correct view.
- final boolean isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
+ final boolean isLowPriority;
+ if (inflaterParams != null) {
+ // NEW pipeline
+ isLowPriority = inflaterParams.isLowPriority();
+ } else {
+ // LEGACY pipeline
+ mFeatureFlags.checkLegacyPipelineEnabled();
+ // If this is our first time inflating, we don't actually know the groupings for real
+ // yet, so we might actually inflate a low priority content view incorrectly here and
+ // have to correct it later in the pipeline. On subsequent inflations (i.e. updates),
+ // this should inflate the correct view.
+ isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
+ }
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
index 518c3f1d1948..dd1f9485a4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.inflation;
+package com.android.systemui.statusbar.notification.collection.legacy;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
@@ -61,6 +59,7 @@ public class LowPriorityInflationHelper {
public void recheckLowPriorityViewAndInflate(
NotificationEntry entry,
ExpandableNotificationRow row) {
+ mFeatureFlags.checkLegacyPipelineEnabled();
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
final boolean shouldBeLowPriority = shouldUseLowPriorityView(entry);
if (!row.isRemoved() && row.isLowPriority() != shouldBeLowPriority) {
@@ -74,12 +73,7 @@ public class LowPriorityInflationHelper {
* Whether the notification should inflate a low priority version of its content views.
*/
public boolean shouldUseLowPriorityView(NotificationEntry entry) {
- boolean isGroupChild;
- if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- isGroupChild = (entry.getParent() != GroupEntry.ROOT_ENTRY);
- } else {
- isGroupChild = mGroupManager.isChildInGroup(entry);
- }
- return entry.isAmbient() && !isGroupChild;
+ mFeatureFlags.checkLegacyPipelineEnabled();
+ return entry.isAmbient() && !mGroupManager.isChildInGroup(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index 6424e37ad328..8444287cbf60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -27,8 +27,7 @@ data class NotifSection(
val label: String
get() = "Section($index, $bucket, \"${sectioner.name}\")"
- val headerController: NodeController?
- get() = sectioner.headerNodeController
+ val headerController: NodeController? = sectioner.headerNodeController
@PriorityBucket val bucket: Int = sectioner.bucket
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 027ac0f66b35..798bfe7f39d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -82,8 +82,8 @@ public class PipelineState {
public static final int STATE_GROUPING = 4;
public static final int STATE_TRANSFORMING = 5;
public static final int STATE_GROUP_STABILIZING = 6;
- public static final int STATE_FINALIZE_FILTERING = 7;
- public static final int STATE_SORTING = 8;
+ public static final int STATE_SORTING = 7;
+ public static final int STATE_FINALIZE_FILTERING = 8;
public static final int STATE_FINALIZING = 9;
@IntDef(prefix = { "STATE_" }, value = {
@@ -94,8 +94,8 @@ public class PipelineState {
STATE_GROUPING,
STATE_TRANSFORMING,
STATE_GROUP_STABILIZING,
- STATE_FINALIZE_FILTERING,
STATE_SORTING,
+ STATE_FINALIZE_FILTERING,
STATE_FINALIZING,
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
index 520791c91803..cb2d3cb97468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
@@ -60,4 +60,19 @@ public abstract class NotifStabilityManager extends Pluggable<NotifStabilityMana
* @return if can re-order
*/
public abstract boolean isEntryReorderingAllowed(ListEntry entry);
+
+ /**
+ * Called by the pipeline to determine if every call to the other stability methods would
+ * return true, regardless of parameters. This allows the pipeline to skip any pieces of
+ * work related to stability.
+ *
+ * @return true if all other methods will return true for any parameters.
+ */
+ public abstract boolean isEveryChangeAllowed();
+
+ /**
+ * Called by the pipeline to inform the stability manager that an entry reordering was indeed
+ * suppressed as the result of a previous call to {@link #isEntryReorderingAllowed(ListEntry)}.
+ */
+ public abstract void onEntryReorderSuppressed();
}
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 bc79db36c3ea..a97a54a50da5 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
@@ -4631,6 +4631,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void addContainerViewAt(View v, int index) {
Assert.isMainThread();
+ ensureRemovedFromTransientContainer(v);
addView(v, index);
if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
mController.updateShowEmptyShadeView();
@@ -4640,6 +4641,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateSpeedBumpIndex();
}
+ private void ensureRemovedFromTransientContainer(View v) {
+ if (v.getParent() == this && v instanceof SectionHeaderView) {
+ ExpandableView expandableView = (ExpandableView) v;
+ ViewGroup transientContainer = expandableView.getTransientContainer();
+ // If the child is animating away, it will still have a parent, so
+ // detach it first
+ // TODO: We should really cancel the active animations here. This will
+ // happen automatically when the view's intro animation starts, but
+ // it's a fragile link.
+ if (transientContainer != null) {
+ transientContainer.removeTransientView(v);
+ expandableView.setTransientContainer(null);
+ }
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index e26e69f9381a..395369898e91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2342,7 +2342,7 @@ public class NotificationPanelViewController extends PanelViewController {
mQs.setExpanded(mQsExpanded);
}
- private void setQsExpansion(float height) {
+ void setQsExpansion(float height) {
height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
@@ -2386,7 +2386,13 @@ public class NotificationPanelViewController extends PanelViewController {
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
setQSClippingBounds();
- mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+
+ // Only need to notify the notification stack when we're not in split screen mode. If we
+ // do, then the notification panel starts scrolling along with the QS.
+ if (!mShouldUseSplitNotificationShade) {
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+ }
+
mDepthController.setQsPanelExpansion(qsExpansionFraction);
if (mCommunalViewController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index afa333ac68b3..1108f5ef1337 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -128,12 +128,12 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.AutoReinflateContainer;
+import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogTags;
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
@@ -255,7 +255,7 @@ import javax.inject.Named;
import dagger.Lazy;
/** */
-public class StatusBar extends SystemUI implements
+public class StatusBar extends CoreStartable implements
ActivityStarter,
LifecycleOwner {
public static final boolean MULTIUSER_DEBUG = false;
@@ -2385,6 +2385,8 @@ public class StatusBar extends SystemUI implements
if (mLightRevealScrim != null) {
pw.println(
+ "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect());
+ pw.println(
"mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
}
@@ -3370,17 +3372,24 @@ public class StatusBar extends SystemUI implements
return;
}
- if (wakingUp && mWakefulnessLifecycle.getLastWakeReason()
- == PowerManager.WAKE_REASON_POWER_BUTTON
- || !wakingUp && mWakefulnessLifecycle.getLastSleepReason()
- == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
+ final boolean wakingUpFromPowerButton = wakingUp
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
+ && mWakefulnessLifecycle.getLastWakeReason()
+ == PowerManager.WAKE_REASON_POWER_BUTTON;
+ final boolean sleepingFromPowerButton = !wakingUp
+ && mWakefulnessLifecycle.getLastSleepReason()
+ == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
} else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
// If we're going to sleep, but it's not from the power button, use the default reveal.
// If we're waking up, only use the default reveal if the biometric controller didn't
// already set it to the circular reveal because we're waking up from a fingerprint/face
// auth.
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 37a763b740e1..8ea7d62c9c74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -22,7 +22,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -36,7 +36,7 @@ import dagger.Lazy;
* Serves as a collection of UI components, rather than showing its own UI.
*/
@SysUISingleton
-public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
+public class TvStatusBar extends CoreStartable implements CommandQueue.Callbacks {
private final CommandQueue mCommandQueue;
private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
index 7c6f86aceeb0..e25a10507825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
@@ -24,9 +24,9 @@ import android.app.NotificationManager
import android.content.Context
import com.android.internal.messages.nano.SystemMessageProto
import com.android.internal.net.VpnConfig
+import com.android.systemui.CoreStartable
import com.android.systemui.Dependency
import com.android.systemui.R
-import com.android.systemui.SystemUI
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.SecurityController
import javax.inject.Inject
@@ -35,7 +35,7 @@ import javax.inject.Inject
* Observes if a vpn connection is active and displays a notification to the user
*/
@SysUISingleton
-class VpnStatusObserver @Inject constructor(context: Context) : SystemUI(context),
+class VpnStatusObserver @Inject constructor(context: Context) : CoreStartable(context),
SecurityController.SecurityControllerCallback {
private var vpnConnected = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
index d985803c2b39..8026ba517820 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
@@ -24,7 +24,7 @@ import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.SparseArray;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.statusbar.NotificationListener;
import javax.inject.Inject;
@@ -32,7 +32,7 @@ import javax.inject.Inject;
/**
* Keeps track of the notifications on TV.
*/
-public class TvNotificationHandler extends SystemUI implements
+public class TvNotificationHandler extends CoreStartable implements
NotificationListener.NotificationHandler {
private static final String TAG = "TvNotificationHandler";
private final NotificationListener mNotificationListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index c9bc7e6c8e5c..892fedcc8ce2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -25,7 +25,7 @@ import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.util.Log;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -35,7 +35,7 @@ import javax.inject.Inject;
* Offers control methods for the notification panel handler on TV devices.
*/
@SysUISingleton
-public class TvNotificationPanel extends SystemUI implements CommandQueue.Callbacks {
+public class TvNotificationPanel extends CoreStartable implements CommandQueue.Callbacks {
private static final String TAG = "TvNotificationPanel";
private final CommandQueue mCommandQueue;
private final String mNotificationHandlerPackage;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c5f463f82bba..adfff4af2775 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -53,8 +53,8 @@ import android.util.TypedValue;
import androidx.annotation.NonNull;
import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -93,7 +93,7 @@ import javax.inject.Inject;
* associated work profiles
*/
@SysUISingleton
-public class ThemeOverlayController extends SystemUI implements Dumpable {
+public class ThemeOverlayController extends CoreStartable implements Dumpable {
protected static final String TAG = "ThemeOverlayController";
private static final boolean DEBUG = true;
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 42f66875e7a1..0758f8fc4fab 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -37,7 +37,7 @@ import android.widget.ToastPresenter;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -49,7 +49,7 @@ import javax.inject.Inject;
* Controls display of text toasts.
*/
@SysUISingleton
-public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+public class ToastUI extends CoreStartable implements CommandQueue.Callbacks {
// values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 353333f714d4..e59d2f233804 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -16,7 +16,7 @@
package com.android.systemui.tv;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
@@ -33,5 +33,5 @@ interface TvSystemUIBinder {
@Binds
@IntoMap
@ClassKey(TvNotificationHandler.class)
- SystemUI bindTvNotificationHandler(TvNotificationHandler systemui);
+ CoreStartable bindTvNotificationHandler(TvNotificationHandler systemui);
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index cc2c2083cbab..65106f1df93c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -41,7 +41,9 @@ annotation class SysUIUnfoldScope
* no objects will get constructed if these parameters are empty.
*/
@Module(subcomponents = [SysUIUnfoldComponent::class])
-object SysUIUnfoldModule {
+class SysUIUnfoldModule {
+ constructor() {}
+
@Provides
@SysUISingleton
fun provideSysUIUnfoldComponent(
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 755372b4b0f5..881ac08c541c 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -44,12 +44,12 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.util.NotificationChannels;
import java.util.List;
-public class StorageNotification extends SystemUI {
+public class StorageNotification extends CoreStartable {
private static final String TAG = "StorageNotification";
private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
@@ -224,7 +224,7 @@ public class StorageNotification extends SystemUI {
.setCategory(Notification.CATEGORY_SYSTEM)
.setDeleteIntent(buildSnoozeIntent(fsUuid))
.extend(new Notification.TvExtender());
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(fsUuid, SystemMessage.NOTE_STORAGE_PRIVATE,
builder.build(), UserHandle.ALL);
@@ -252,7 +252,7 @@ public class StorageNotification extends SystemUI {
.setLocalOnly(true)
.setCategory(Notification.CATEGORY_ERROR)
.extend(new Notification.TvExtender());
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(disk.getId(), SystemMessage.NOTE_STORAGE_DISK,
builder.build(), UserHandle.ALL);
@@ -527,7 +527,7 @@ public class StorageNotification extends SystemUI {
.setCategory(Notification.CATEGORY_PROGRESS)
.setProgress(100, status, false)
.setOngoing(true);
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
builder.build(), UserHandle.ALL);
@@ -577,7 +577,7 @@ public class StorageNotification extends SystemUI {
.setLocalOnly(true)
.setCategory(Notification.CATEGORY_SYSTEM)
.setAutoCancel(true);
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
builder.build(), UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index f97f4d0edc24..ce7e4cf82081 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -23,13 +23,13 @@ import android.net.Uri;
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import java.util.Arrays;
-public class NotificationChannels extends SystemUI {
+public class NotificationChannels extends CoreStartable {
public static String ALERTS = "ALR";
public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
public static String GENERAL = "GEN";
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 431899460586..612b7cb10007 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -45,9 +45,9 @@ import android.util.LongSparseArray;
import android.view.View;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -565,7 +565,7 @@ public class GarbageMonitor implements Dumpable {
/** */
@SysUISingleton
- public static class Service extends SystemUI implements Dumpable {
+ public static class Service extends CoreStartable implements Dumpable {
private final GarbageMonitor mGarbageMonitor;
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 5aa3617b1651..11725ef4867e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -33,7 +33,11 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IVolumeController;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
import android.media.VolumePolicy;
+import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession.Token;
import android.net.Uri;
@@ -71,6 +75,7 @@ import com.android.systemui.util.concurrency.ThreadFactory;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -118,6 +123,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private final Context mContext;
private final Looper mWorkerLooper;
private final PackageManager mPackageManager;
+ private final MediaRouter2Manager mRouter2Manager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private AudioManager mAudio;
private IAudioService mAudioService;
@@ -179,6 +185,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mWorkerLooper = theadFactory.buildLooperOnNewThread(
VolumeDialogControllerImpl.class.getSimpleName());
mWorker = new W(mWorkerLooper);
+ mRouter2Manager = MediaRouter2Manager.getInstance(mContext);
mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext);
mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
mAudio = audioManager;
@@ -1150,16 +1157,16 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
private int mNextStream = DYNAMIC_STREAM_START_INDEX;
- private final boolean mShowRemoteSessions;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
public MediaSessionsCallbacks(Context context) {
- mShowRemoteSessions = context.getResources().getBoolean(
- com.android.internal.R.bool.config_volumeShowRemoteSessions);
+ mVolumeAdjustmentForRemoteGroupSessions = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
@Override
public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
- if (mShowRemoteSessions) {
+ if (showForSession(token)) {
addStream(token, "onRemoteUpdate");
int stream = 0;
@@ -1191,7 +1198,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Override
public void onRemoteVolumeChanged(Token token, int flags) {
- if (mShowRemoteSessions) {
+ if (showForSession(token)) {
addStream(token, "onRemoteVolumeChanged");
int stream = 0;
synchronized (mRemoteStreams) {
@@ -1215,7 +1222,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
@Override
public void onRemoteRemoved(Token token) {
- if (mShowRemoteSessions) {
+ if (showForSession(token)) {
int stream = 0;
synchronized (mRemoteStreams) {
if (!mRemoteStreams.containsKey(token)) {
@@ -1234,14 +1241,41 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
public void setStreamVolume(int stream, int level) {
- if (mShowRemoteSessions) {
- final Token t = findToken(stream);
- if (t == null) {
- Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
- return;
+ final Token token = findToken(stream);
+ if (token == null) {
+ Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
+ return;
+ }
+ if (showForSession(token)) {
+ mMediaSessions.setVolume(token, level);
+ }
+ }
+
+ private boolean showForSession(Token token) {
+ if (mVolumeAdjustmentForRemoteGroupSessions) {
+ return true;
+ }
+ MediaController ctr = new MediaController(mContext, token);
+ String packageName = ctr.getPackageName();
+ List<RoutingSessionInfo> sessions =
+ mRouter2Manager.getRoutingSessions(packageName);
+ boolean foundNonSystemSession = false;
+ boolean isGroup = false;
+ for (RoutingSessionInfo session : sessions) {
+ if (!session.isSystemSession()) {
+ foundNonSystemSession = true;
+ int selectedRouteCount = session.getSelectedRoutes().size();
+ if (selectedRouteCount > 1) {
+ isGroup = true;
+ break;
+ }
}
- mMediaSessions.setVolume(t, level);
}
+ if (!foundNonSystemSession) {
+ Log.d(TAG, "No routing session for " + packageName);
+ return false;
+ }
+ return !isGroup;
}
private Token findToken(int stream) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index c378e3bd76c9..8007ed3b866a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -21,8 +21,8 @@ import android.content.res.Configuration;
import android.os.Handler;
import android.util.Log;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.tiles.DndTile;
@@ -32,7 +32,7 @@ import java.io.PrintWriter;
import javax.inject.Inject;
@SysUISingleton
-public class VolumeUI extends SystemUI {
+public class VolumeUI extends CoreStartable {
private static final String TAG = "VolumeUI";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 74611cce6f87..66c70edbc3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -40,8 +40,8 @@ import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
@@ -92,7 +92,7 @@ import javax.inject.Inject;
* -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
*/
@SysUISingleton
-public final class WMShell extends SystemUI
+public final class WMShell extends CoreStartable
implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
private static final String TAG = WMShell.class.getName();
private static final int INVALID_SYSUI_STATE_MASK =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ee6f70ec7fc..ff5960bc33ce 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -472,7 +472,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
- verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index fe5633eb9df8..6ddfbb2f430f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -55,10 +55,9 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.SparseIntArray;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.View;
@@ -101,6 +100,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private AccessibilityManager mAccessibilityManager;
@Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+ private SwitchListenerStub mSwitchListener;
private TestableWindowManager mWindowManager;
private ViewPropertyAnimator mViewPropertyAnimator;
private MagnificationModeSwitch mMagnificationModeSwitch;
@@ -112,6 +112,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
final WindowManager wm = mContext.getSystemService(WindowManager.class);
+ mSwitchListener = new SwitchListenerStub();
mWindowManager = spy(new TestableWindowManager(wm));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
@@ -130,7 +131,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(Choreographer.FrameCallback.class));
mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView,
- mSfVsyncFrameProvider);
+ mSfVsyncFrameProvider, mSwitchListener);
assertNotNull(mTouchListener);
}
@@ -326,8 +327,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void performDragging_showMagnificationButton_updateViewLayout() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
resetAndStubMockImageViewAndAnimator();
- final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
// Perform dragging
final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10;
@@ -345,7 +344,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
downTime, downTime, ACTION_UP, 100 + offset, 100));
- assertModeUnchanged(previousMode);
+ assertModeUnchanged();
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@@ -353,8 +352,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void performSingleTapActionCanceled_showButtonAnimation() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
resetAndStubMockImageViewAndAnimator();
- final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
final long downTime = SystemClock.uptimeMillis();
mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
@@ -363,7 +360,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
downTime, downTime, ACTION_CANCEL, 100, 100));
- assertModeUnchanged(previousMode);
+ assertModeUnchanged();
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@@ -371,8 +368,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void performDraggingActionCanceled_showButtonAnimation() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
resetAndStubMockImageViewAndAnimator();
- final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
// Perform dragging
final long downTime = SystemClock.uptimeMillis();
@@ -385,7 +380,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
downTime, downTime, ACTION_CANCEL, 100 + offset, 100));
- assertModeUnchanged(previousMode);
+ assertModeUnchanged();
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@@ -529,10 +524,9 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
}
- private void assertModeUnchanged(int expectedMode) {
- final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
- assertEquals(expectedMode, actualMode);
+ private void assertModeUnchanged() {
+ assertEquals(SwitchListenerStub.MODE_INVALID,
+ mSwitchListener.getChangedMode(mContext.getDisplayId()));
}
private void assertShowFadingAnimation(float alpha) {
@@ -594,9 +588,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
verify(mSpyImageView).setImageResource(
getIconResId(expectedMode));
verify(mWindowManager).removeView(mSpyImageView);
- final int actualMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
- assertEquals(expectedMode, actualMode);
+ final int changedMode = mSwitchListener.getChangedMode(mContext.getDisplayId());
+ assertEquals(expectedMode, changedMode);
}
private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
@@ -621,4 +614,20 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
assertEquals(expectedX, layoutParams.x);
assertEquals(expectedY, layoutParams.y);
}
+
+ private static class SwitchListenerStub implements MagnificationModeSwitch.SwitchListener {
+
+ private static final int MODE_INVALID = -1;
+
+ private final SparseIntArray mModes = new SparseIntArray();
+
+ @Override
+ public void onSwitch(int displayId, int magnificationMode) {
+ mModes.put(displayId, magnificationMode);
+ }
+
+ int getChangedMode(int displayId) {
+ return mModes.get(displayId, MODE_INVALID);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index 9fa5b87af3b4..216f63fce885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -22,27 +22,32 @@ import android.content.pm.ActivityInfo;
import android.hardware.display.DisplayManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.Display;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
/** Tests the ModeSwitchesController. */
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class ModeSwitchesControllerTest extends SysuiTestCase {
private FakeSwitchSupplier mSupplier;
- @Mock
private MagnificationModeSwitch mModeSwitch;
private ModeSwitchesController mModeSwitchesController;
+ @Mock
+ private MagnificationModeSwitch.SwitchListener mListener;
@Before
@@ -50,6 +55,13 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mSupplier = new FakeSwitchSupplier(mContext.getSystemService(DisplayManager.class));
mModeSwitchesController = new ModeSwitchesController(mSupplier);
+ mModeSwitchesController.setSwitchListenerDelegate(mListener);
+ mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, mModeSwitchesController));
+ }
+
+ @After
+ public void tearDown() {
+ mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
}
@Test
@@ -79,6 +91,18 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
verify(mModeSwitch).onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
}
+
+ @Test
+ public void testOnSwitchClick_showWindowModeButton_invokeListener() {
+ mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ mModeSwitch.onSingleTap();
+
+ verify(mListener).onSwitch(mContext.getDisplayId(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ }
+
private class FakeSwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
FakeSwitchSupplier(DisplayManager displayManager) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 6ef7cc3f0af8..fb1716aed474 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -22,6 +22,8 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_M
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -99,17 +101,19 @@ public class WindowMagnificationTest extends SysuiTestCase {
}
@Test
- public void requestWindowMagnificationConnection_setWindowMagnificationConnection() {
+ public void requestWindowMagnificationConnection_setConnectionAndListener() {
mCommandQueue.requestWindowMagnificationConnection(true);
waitForIdleSync();
verify(mAccessibilityManager).setWindowMagnificationConnection(any(
IWindowMagnificationConnection.class));
+ verify(mModeSwitchesController).setSwitchListenerDelegate(notNull());
mCommandQueue.requestWindowMagnificationConnection(false);
waitForIdleSync();
- verify(mAccessibilityManager).setWindowMagnificationConnection(null);
+ verify(mAccessibilityManager).setWindowMagnificationConnection(isNull());
+ verify(mModeSwitchesController).setSwitchListenerDelegate(isNull());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 2fa32ba1fe75..634763866d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -53,8 +53,6 @@ import java.io.StringWriter;
public class FeatureFlagManagerTest extends SysuiTestCase {
FeatureFlagManager mFeatureFlagManager;
- @Mock private FlagManager mFlagManager;
- @Mock private SecureSettings mSecureSettings;
@Mock private Context mContext;
@Mock private DumpManager mDumpManager;
@@ -62,14 +60,11 @@ public class FeatureFlagManagerTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlagManager = new FeatureFlagManager(mSecureSettings, mContext, mDumpManager);
+ mFeatureFlagManager = new FeatureFlagManager(mDumpManager);
}
@After
public void onFinished() {
- // SecureSettings and Context are provided for constructor consistency with the
- // debug version of the FeatureFlagManager, but should never be used.
- verifyZeroInteractions(mSecureSettings, mContext);
// The dump manager should be registered with even for the release version, but that's it.
verify(mDumpManager).registerDumpable(anyString(), any());
verifyNoMoreInteractions(mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
deleted file mode 100644
index 30e9b51ea47e..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-@SmallTest
-public class FeatureFlagsTest extends SysuiTestCase {
-
- @Mock Resources mResources;
- @Mock FlagReader mFeatureFlagReader;
-
- private FeatureFlags mFeatureFlags;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- when(mFeatureFlagReader.isEnabled(anyInt(), anyBoolean())).thenAnswer(
- (Answer<Boolean>) invocation -> invocation.getArgument(1));
-
- mFeatureFlags = new FeatureFlags(mResources, mFeatureFlagReader, getContext());
- }
-
- @Test
- public void testAddListener() {
- Flag<?> flag = new BooleanFlag(1);
- mFeatureFlags.addFlag(flag);
-
- // Assert and capture that a plugin listener was added.
- ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
- ArgumentCaptor.forClass(FlagReader.Listener.class);
- verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
- FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
- // Signal a change. No listeners, so no real effect.
- pluginListener.onFlagChanged(flag.getId());
-
- // Add a listener for the flag
- final Flag<?>[] changedFlag = {null};
- FeatureFlags.Listener listener = f -> changedFlag[0] = f;
- mFeatureFlags.addFlagListener(flag, listener);
-
- // No changes seen yet.
- assertThat(changedFlag[0]).isNull();
-
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
-
- // Assert that the change was for the correct flag.
- assertThat(changedFlag[0]).isEqualTo(flag);
- }
-
- @Test
- public void testRemoveListener() {
- Flag<?> flag = new BooleanFlag(1);
- mFeatureFlags.addFlag(flag);
-
- // Assert and capture that a plugin listener was added.
- ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
- ArgumentCaptor.forClass(FlagReader.Listener.class);
- verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
- FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
- // Add a listener for the flag
- final Flag<?>[] changedFlag = {null};
- FeatureFlags.Listener listener = f -> changedFlag[0] = f;
- mFeatureFlags.addFlagListener(flag, listener);
-
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
-
- // Assert that the change was for the correct flag.
- assertThat(changedFlag[0]).isEqualTo(flag);
-
- changedFlag[0] = null;
-
- // Now remove the listener.
- mFeatureFlags.removeFlagListener(flag, listener);
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
- // Assert that the change was not triggered
- assertThat(changedFlag[0]).isNull();
- }
-
- @Test
- public void testBooleanDefault() {
- BooleanFlag flag = new BooleanFlag(1, true);
-
- mFeatureFlags.addFlag(flag);
-
- assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
- }
-
- @Test
- public void testBooleanResourceOverlay() {
- int resourceId = 12;
- BooleanFlag flag = new BooleanFlag(1, false, resourceId);
- when(mResources.getBoolean(resourceId)).thenReturn(true);
- when(mResources.getResourceEntryName(resourceId)).thenReturn("flag");
-
- mFeatureFlags.addFlag(flag);
-
- assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5e73dbcbc95d..d64319b278b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -212,6 +212,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
@Test
public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() {
// GIVEN fp sensor location is not available pre-init
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
when(mAuthController.getUdfpsProps()).thenReturn(null);
mLockIconViewController.init();
@@ -232,7 +233,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
}
@Test
- public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() {
+ public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
// GIVEN Udpfs sensor location is available
setupUdfps();
@@ -247,9 +248,9 @@ public class LockIconViewControllerTest extends SysuiTestCase {
}
@Test
- public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() {
- // GIVEN Udfps sensor location is not available
- when(mAuthController.getUdfpsSensorLocation()).thenReturn(null);
+ public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
+ // GIVEN Udfps sensor location is not supported
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
mLockIconViewController.init();
captureAttachListener();
@@ -365,6 +366,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
}
private Pair<Integer, PointF> setupUdfps() {
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
final FingerprintSensorPropertiesInternal fpProps =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
index bc86ef98c6fe..8cd7d94d8952 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -22,6 +22,7 @@ import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
+import com.android.internal.graphics.cam.Cam;
import com.android.systemui.SysuiTestCase;
import org.junit.Assert;
@@ -90,4 +91,13 @@ public class ColorSchemeTest extends SysuiTestCase {
List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a, 0xffbe0000, 0xffcc040f));
}
+
+ @Test
+ public void testTertiaryHueWrapsProperly() {
+ int colorInt = 0xffB3588A; // H350 C50 T50
+ ColorScheme colorScheme = new ColorScheme(colorInt, false /* darkTheme */);
+ int tertiaryMid = colorScheme.getAccent3().get(colorScheme.getAccent3().size() / 2);
+ Cam cam = Cam.fromInt(tertiaryMid);
+ Assert.assertEquals(cam.getHue(), 50.0, 10.0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 01f7fae05f76..cb0d87a20f89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -701,7 +701,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// GIVEN fingerprint is also running (not udfps)
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
mController.setVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index cf58c63e3d26..acd62b2fabe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index fc94262465b1..b91f7e6b6169 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -30,6 +30,7 @@ import android.service.notification.StatusBarNotification;
import com.android.internal.logging.InstanceId;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.util.time.FakeSystemClock;
import java.util.ArrayList;
@@ -53,6 +54,7 @@ public class NotificationEntryBuilder {
/* ListEntry properties */
private GroupEntry mParent;
+ private NotifSection mNotifSection;
/* If set, use this creation time instead of mClock.uptimeMillis */
private long mCreationTime = -1;
@@ -71,6 +73,11 @@ public class NotificationEntryBuilder {
mCreationTime = source.getCreationTime();
}
+ /** Update an the parent on an existing entry */
+ public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
+ entry.setParent(parent);
+ }
+
/** Build a new instance of NotificationEntry */
public NotificationEntry build() {
return buildOrApply(null);
@@ -103,6 +110,7 @@ public class NotificationEntryBuilder {
/* ListEntry properties */
entry.setParent(mParent);
+ entry.getAttachState().setSection(mNotifSection);
entry.getAttachState().setStableIndex(mStableIndex);
return entry;
}
@@ -116,6 +124,14 @@ public class NotificationEntryBuilder {
}
/**
+ * Sets the parent.
+ */
+ public NotificationEntryBuilder setSection(@Nullable NotifSection section) {
+ mNotifSection = section;
+ return this;
+ }
+
+ /**
* Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be
* ignored.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 913ffd44437a..b254ed4e3f2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection;
import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpTree;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -44,7 +46,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -79,10 +81,11 @@ import org.mockito.Spy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@SmallTest
@@ -124,7 +127,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mListBuilder.attach(mNotifCollection);
- mStabilityManager = new TestableStabilityManager();
+ mStabilityManager = spy(new TestableStabilityManager());
mListBuilder.setNotifStabilityManager(mStabilityManager);
Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
@@ -618,26 +621,53 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Test
public void testNotifSectionsChildrenUpdated() {
- AtomicBoolean validChildren = new AtomicBoolean(false);
+ ArrayList<ListEntry> pkg1Entries = new ArrayList<>();
+ ArrayList<ListEntry> pkg2Entries = new ArrayList<>();
+ ArrayList<ListEntry> pkg3Entries = new ArrayList<>();
final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1) {
- @Nullable
@Override
public void onEntriesUpdated(List<ListEntry> entries) {
super.onEntriesUpdated(entries);
- validChildren.set(entries.size() == 2);
+ pkg1Entries.addAll(entries);
+ }
+ });
+ final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2) {
+ @Override
+ public void onEntriesUpdated(List<ListEntry> entries) {
+ super.onEntriesUpdated(entries);
+ pkg2Entries.addAll(entries);
+ }
+ });
+ final NotifSectioner pkg3Sectioner = spy(new PackageSectioner(PACKAGE_3) {
+ @Override
+ public void onEntriesUpdated(List<ListEntry> entries) {
+ super.onEntriesUpdated(entries);
+ pkg3Entries.addAll(entries);
}
});
- mListBuilder.setSectioners(asList(pkg1Sectioner));
+ mListBuilder.setSectioners(asList(pkg1Sectioner, pkg2Sectioner, pkg3Sectioner));
- addNotif(0, PACKAGE_4);
+ addNotif(0, PACKAGE_1);
addNotif(1, PACKAGE_1);
- addNotif(2, PACKAGE_1);
+ addNotif(2, PACKAGE_3);
addNotif(3, PACKAGE_3);
+ addNotif(4, PACKAGE_3);
dispatchBuild();
- verify(pkg1Sectioner, times(1)).onEntriesUpdated(any());
- assertTrue(validChildren.get());
+ verify(pkg1Sectioner).onEntriesUpdated(any());
+ verify(pkg2Sectioner).onEntriesUpdated(any());
+ verify(pkg3Sectioner).onEntriesUpdated(any());
+ assertThat(pkg1Entries).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1)
+ ).inOrder();
+ assertThat(pkg2Entries).isEmpty();
+ assertThat(pkg3Entries).containsExactly(
+ mEntrySet.get(2),
+ mEntrySet.get(3),
+ mEntrySet.get(4)
+ ).inOrder();
}
@Test
@@ -835,13 +865,13 @@ public class ShadeListBuilderTest extends SysuiTestCase {
.onBeforeTransformGroups(anyList());
inOrder.verify(promoter, atLeastOnce())
.shouldPromoteToTopLevel(any(NotificationEntry.class));
- inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
- inOrder.verify(preRenderFilter, atLeastOnce())
- .shouldFilterOut(any(NotificationEntry.class), anyLong());
inOrder.verify(mOnBeforeSortListener).onBeforeSort(anyList());
inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
inOrder.verify(comparator, atLeastOnce())
.compare(any(ListEntry.class), any(ListEntry.class));
+ inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
+ inOrder.verify(preRenderFilter, atLeastOnce())
+ .shouldFilterOut(any(NotificationEntry.class), anyLong());
inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
inOrder.verify(mOnRenderListListener).onRenderList(anyList());
}
@@ -947,13 +977,11 @@ public class ShadeListBuilderTest extends SysuiTestCase {
);
// THEN all the new notifs, including the new GroupEntry, are passed to the listener
- assertEquals(
- asList(
- mEntrySet.get(0),
- mBuiltList.get(1),
- mEntrySet.get(4)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mBuiltList.get(1),
+ mEntrySet.get(4)
+ ).inOrder(); // Order is a bonus because this listener is before sort
}
@Test
@@ -993,14 +1021,12 @@ public class ShadeListBuilderTest extends SysuiTestCase {
);
// THEN all the new notifs, including the new GroupEntry, are passed to the listener
- assertEquals(
- asList(
- mEntrySet.get(0),
- mBuiltList.get(2),
- mEntrySet.get(7),
- mEntrySet.get(1)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mBuiltList.get(2),
+ mEntrySet.get(7)
+ ).inOrder(); // Order is a bonus because this listener is before sort
}
@Test
@@ -1091,9 +1117,93 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
+ public void testFinalizeFilteringGroupSummaryDoesNotBreakSort() {
+ // GIVEN children from 3 packages, with one in the middle of the sort order being a group
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_1);
+ addNotif(4, PACKAGE_2);
+ addNotif(5, PACKAGE_3);
+ addGroupSummary(6, PACKAGE_2, GROUP_1);
+ addGroupChild(7, PACKAGE_2, GROUP_1);
+ addGroupChild(8, PACKAGE_2, GROUP_1);
+
+ // GIVEN that they should be sorted by package
+ mListBuilder.setComparators(asList(
+ new HypeComparator(PACKAGE_1),
+ new HypeComparator(PACKAGE_2),
+ new HypeComparator(PACKAGE_3)
+ ));
+
+ // WHEN a finalize filter removes the summary
+ mListBuilder.addFinalizeFilter(new NotifFilter("Test") {
+ @Override
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+ return entry == notif(6).entry;
+ }
+ });
+
+ dispatchBuild();
+
+ // THEN the notifications remain ordered by package, even though the children were promoted
+ verifyBuiltList(
+ notif(0),
+ notif(3),
+ notif(1),
+ notif(4),
+ notif(7), // promoted child
+ notif(8), // promoted child
+ notif(2),
+ notif(5)
+ );
+ }
+
+ @Test
+ public void testFinalizeFilteringGroupChildDoesNotBreakSort() {
+ // GIVEN children from 3 packages, with one in the middle of the sort order being a group
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_1);
+ addNotif(4, PACKAGE_2);
+ addNotif(5, PACKAGE_3);
+ addGroupSummary(6, PACKAGE_2, GROUP_1);
+ addGroupChild(7, PACKAGE_2, GROUP_1);
+ addGroupChild(8, PACKAGE_2, GROUP_1);
+
+ // GIVEN that they should be sorted by package
+ mListBuilder.setComparators(asList(
+ new HypeComparator(PACKAGE_1),
+ new HypeComparator(PACKAGE_2),
+ new HypeComparator(PACKAGE_3)
+ ));
+
+ // WHEN a finalize filter one of the 2 children from a group
+ mListBuilder.addFinalizeFilter(new NotifFilter("Test") {
+ @Override
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+ return entry == notif(7).entry;
+ }
+ });
+
+ dispatchBuild();
+
+ // THEN the notifications remain ordered by package, even though the children were promoted
+ verifyBuiltList(
+ notif(0),
+ notif(3),
+ notif(1),
+ notif(4),
+ notif(8), // promoted child
+ notif(2),
+ notif(5)
+ );
+ }
+
+ @Test
public void testBrokenGroupNotificationOrdering() {
// GIVEN two group children with different sections & without a summary yet
-
addGroupChild(0, PACKAGE_2, GROUP_1);
addNotif(1, PACKAGE_1);
addGroupChild(2, PACKAGE_2, GROUP_1);
@@ -1224,13 +1334,11 @@ public class ShadeListBuilderTest extends SysuiTestCase {
dispatchBuild();
// THEN all the new notifs are passed to the listener out of order
- assertEquals(
- asList(
- mEntrySet.get(0),
- mEntrySet.get(1),
- mEntrySet.get(2)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mEntrySet.get(2)
+ ).inOrder(); // Checking out-of-order input to validate sorted output
// THEN the final list is in order
verifyBuiltList(
@@ -1256,13 +1364,11 @@ public class ShadeListBuilderTest extends SysuiTestCase {
dispatchBuild();
// THEN all the new notifs are passed to the listener
- assertEquals(
- asList(
- mEntrySet.get(0),
- mEntrySet.get(1),
- mEntrySet.get(2)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mEntrySet.get(2)
+ ).inOrder();
}
@Test
@@ -1365,6 +1471,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
assertOrder("ABCDEFG", "ACDEFBG", "ABCDEFG"); // no change
assertOrder("ABCDEFG", "ACDEFBXZG", "XZABCDEFG"); // Z and X
assertOrder("ABCDEFG", "AXCDEZFBG", "XZABCDEFG"); // Z and X + gap
+ verify(mStabilityManager, times(4)).onEntryReorderSuppressed();
}
@Test
@@ -1373,6 +1480,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG"); // no change
assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG"); // Z and X
assertOrder("ABCDEFG", "AXCDEZFBG", "AXCDEZFBG"); // Z and X + gap
+ verify(mStabilityManager, never()).onEntryReorderSuppressed();
}
@Test
@@ -1410,6 +1518,26 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// THEN no exception thrown
}
+ @Test
+ public void testIsSorted() {
+ Comparator<Integer> intCmp = Integer::compare;
+ assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Collections.singletonList(1), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4, 5), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 1, 1, 1, 1), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 1, 2, 2, 3, 3), intCmp));
+
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(2, 1), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(2, 1, 2), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 1), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 2, 5), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(5, 2, 3, 4, 5), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4, 1), intCmp));
+ }
+
/**
* Adds a notif to the collection that will be passed to the list builder when
* {@link #dispatchBuild()}s is called.
@@ -1815,6 +1943,15 @@ public class ShadeListBuilderTest extends SysuiTestCase {
public boolean isEntryReorderingAllowed(ListEntry entry) {
return mAllowEntryReodering;
}
+
+ @Override
+ public boolean isEveryChangeAllowed() {
+ return mAllowEntryReodering && mAllowGroupChanges && mAllowSectionChanges;
+ }
+
+ @Override
+ public void onEntryReorderSuppressed() {
+ }
}
private static final String PACKAGE_1 = "com.test1";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index bec5174aceba..c3e10aa3178f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
@@ -33,10 +34,12 @@ import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -44,8 +47,11 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
@@ -60,6 +66,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -79,24 +86,36 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor;
@Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor;
@Captor private ArgumentCaptor<NotifInflater.InflationCallback> mCallbackCaptor;
+ @Captor private ArgumentCaptor<NotifInflater.Params> mParamsCaptor;
+ @Mock private NotifSectioner mNotifSectioner;
+ @Mock private NotifSection mNotifSection;
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
+ private final TestableAdjustmentProvider mAdjustmentProvider = new TestableAdjustmentProvider();
+
+ @NonNull
+ private NotificationEntryBuilder getNotificationEntryBuilder() {
+ return new NotificationEntryBuilder().setSection(mNotifSection);
+ }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mEntry = new NotificationEntryBuilder().setParent(ROOT_ENTRY).build();
+ mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build();
mInflationError = new Exception(TEST_MESSAGE);
mErrorManager = new NotifInflationErrorManager();
+ when(mNotifSection.getSectioner()).thenReturn(mNotifSectioner);
+ mAdjustmentProvider.setSectionIsLowPriority(false);
PreparationCoordinator coordinator = new PreparationCoordinator(
mock(PreparationCoordinatorLogger.class),
mNotifInflater,
mErrorManager,
mock(NotifViewBarn.class),
+ mAdjustmentProvider,
mService,
TEST_CHILD_BIND_CUTOFF,
TEST_MAX_GROUP_DELAY);
@@ -150,7 +169,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we inflate it
- verify(mNotifInflater).inflateViews(eq(mEntry), any());
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
// THEN we filter it out until it's done inflating.
assertTrue(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -161,7 +180,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), mCallbackCaptor.capture());
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
mCallbackCaptor.getValue().onInflationFinished(mEntry);
// WHEN notification is updated
@@ -169,7 +188,90 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we rebind it
- verify(mNotifInflater).rebindViews(eq(mEntry), any());
+ verify(mNotifInflater).rebindViews(eq(mEntry), any(), any());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testEntrySmartReplyAdditionWillRebindViews() {
+ // GIVEN an inflated notification
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification ranking now has smart replies
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setSmartReplies("yes").build());
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we rebind it
+ verify(mNotifInflater).rebindViews(eq(mEntry), any(), any());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testEntryChangedToMinimizedSectionWillRebindViews() {
+ // GIVEN an inflated notification
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry),
+ mParamsCaptor.capture(), mCallbackCaptor.capture());
+ assertFalse(mParamsCaptor.getValue().isLowPriority());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification moves to a min priority section
+ mAdjustmentProvider.setSectionIsLowPriority(true);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we rebind it
+ verify(mNotifInflater).rebindViews(eq(mEntry), mParamsCaptor.capture(), any());
+ assertTrue(mParamsCaptor.getValue().isLowPriority());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testMinimizedEntryMovedIntoGroupWillRebindViews() {
+ // GIVEN an inflated, minimized notification
+ mAdjustmentProvider.setSectionIsLowPriority(true);
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry),
+ mParamsCaptor.capture(), mCallbackCaptor.capture());
+ assertTrue(mParamsCaptor.getValue().isLowPriority());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification is moved under a parent
+ NotificationEntryBuilder.setNewParent(mEntry, mock(GroupEntry.class));
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we rebind it as not-minimized
+ verify(mNotifInflater).rebindViews(eq(mEntry), mParamsCaptor.capture(), any());
+ assertFalse(mParamsCaptor.getValue().isLowPriority());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testEntryRankChangeWillNotRebindViews() {
+ // GIVEN an inflated notification
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification ranking changes rank, which does not affect views
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setRank(100).build());
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we do not rebind it
+ verify(mNotifInflater, never()).rebindViews(eq(mEntry), any(), any());
// THEN we do not filter it because it's not the first inflation.
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -180,7 +282,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), mCallbackCaptor.capture());
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
mCallbackCaptor.getValue().onInflationFinished(mEntry);
// THEN it isn't filtered from shade list
@@ -191,13 +293,13 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
public void testCutoffGroupChildrenNotInflated() {
// WHEN there is a new notification group is posted
int id = 0;
- NotificationEntry summary = new NotificationEntryBuilder()
+ NotificationEntry summary = getNotificationEntryBuilder()
.setOverrideGroupKey(TEST_GROUP_KEY)
.setId(id++)
.build();
List<NotificationEntry> children = new ArrayList<>();
for (int i = 0; i < TEST_CHILD_BIND_CUTOFF + 1; i++) {
- NotificationEntry child = new NotificationEntryBuilder()
+ NotificationEntry child = getNotificationEntryBuilder()
.setOverrideGroupKey(TEST_GROUP_KEY)
.setId(id++)
.build();
@@ -224,9 +326,9 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// THEN we inflate up to the cut-off only
for (int i = 0; i < children.size(); i++) {
if (i < TEST_CHILD_BIND_CUTOFF) {
- verify(mNotifInflater).inflateViews(eq(children.get(i)), any());
+ verify(mNotifInflater).inflateViews(eq(children.get(i)), any(), any());
} else {
- verify(mNotifInflater, never()).inflateViews(eq(children.get(i)), any());
+ verify(mNotifInflater, never()).inflateViews(eq(children.get(i)), any(), any());
}
}
}
@@ -236,9 +338,9 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry child0 = group.getChildren().get(0);
@@ -256,9 +358,9 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry summary = group.getSummary();
@@ -281,9 +383,9 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry summary = group.getSummary();
@@ -307,9 +409,9 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry child0 = group.getChildren().get(0);
@@ -324,19 +426,21 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
}
private static class FakeNotifInflater implements NotifInflater {
- private Map<NotificationEntry, InflationCallback> mInflateCallbacks = new HashMap<>();
+ private final Map<NotificationEntry, InflationCallback> mInflateCallbacks = new HashMap<>();
@Override
- public void inflateViews(NotificationEntry entry, InflationCallback callback) {
+ public void inflateViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
mInflateCallbacks.put(entry, callback);
}
@Override
- public void rebindViews(NotificationEntry entry, InflationCallback callback) {
+ public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
}
@Override
- public void abortInflation(NotificationEntry entry) {
+ public void abortInflation(@NonNull NotificationEntry entry) {
}
public InflationCallback getInflateCallback(NotificationEntry entry) {
@@ -365,4 +469,12 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
private static final String TEST_GROUP_KEY = "TEST_GROUP_KEY";
private static final int TEST_CHILD_BIND_CUTOFF = 9;
private static final int TEST_MAX_GROUP_DELAY = 100;
+
+ private class TestableAdjustmentProvider extends NotifUiAdjustmentProvider {
+ private void setSectionIsLowPriority(boolean lowPriority) {
+ setLowPrioritySections(lowPriority
+ ? Collections.singleton(mNotifSection.getSectioner())
+ : Collections.emptyList());
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 2091cf8cb028..abe33aae7fc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,8 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
+import android.app.NotificationManager;
import android.testing.AndroidTestingRunner;
import androidx.annotation.Nullable;
@@ -38,10 +38,12 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -54,7 +56,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -66,12 +67,11 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private HighPriorityProvider mHighPriorityProvider;
+ @Mock private NotifUiAdjustmentProvider mAdjustmentProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NodeController mAlertingHeaderController;
@Mock private NodeController mSilentNodeController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private NotificationListenerService.Ranking mRanking;
- @Mock private StatusBarNotification mSbn;
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
@@ -89,12 +89,14 @@ public class RankingCoordinatorTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mRankingCoordinator = new RankingCoordinator(
- mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
- mSilentHeaderController, mSilentNodeController);
+ mStatusBarStateController,
+ mHighPriorityProvider,
+ mAdjustmentProvider,
+ mAlertingHeaderController,
+ mSilentHeaderController,
+ mSilentNodeController);
mEntry = spy(new NotificationEntryBuilder().build());
- mRanking = spy(getRankingForUnfilteredNotif().build());
- mEntry.setRanking(mRanking);
- when(mEntry.getSbn()).thenReturn(mSbn);
+ mEntry.setRanking(getRankingForUnfilteredNotif().build());
mRankingCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
@@ -109,23 +111,19 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Test
public void testSilentHeaderClearableChildrenUpdate() {
- StatusBarNotification sbn = Mockito.mock(StatusBarNotification.class);
- Mockito.doReturn("key").when(sbn).getKey();
- Mockito.doReturn(Mockito.mock(Notification.class)).when(sbn).getNotification();
- NotificationEntry entry = new NotificationEntryBuilder().setSbn(sbn).build();
- ListEntry listEntry = new ListEntry("key", 0L) {
+ ListEntry listEntry = new ListEntry(mEntry.getKey(), 0L) {
@Nullable
@Override
public NotificationEntry getRepresentativeEntry() {
- return entry;
+ return mEntry;
}
};
- Mockito.doReturn(true).when(sbn).isClearable();
+ setRankingAmbient(false);
+ setSbnClearable(true);
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
- when(mRanking.isAmbient()).thenReturn(false);
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
- mRankingCoordinator.resetClearAllFlags();
- Mockito.doReturn(false).when(sbn).isClearable();
+
+ setSbnClearable(false);
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
}
@@ -204,7 +202,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
public void testIncludeInSectionSilent() {
// GIVEN the entry isn't high priority
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(false);
+ setRankingAmbient(false);
// THEN entry is in the silent section
assertFalse(mAlertingSectioner.isInSection(mEntry));
@@ -213,24 +211,23 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Test
public void testMinSection() {
- when(mEntry.getRanking()).thenReturn(mRanking);
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(true);
+ setRankingAmbient(true);
assertInSection(mEntry, mMinimizedSectioner);
}
@Test
public void testSilentSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(false);
+ setRankingAmbient(false);
assertInSection(mEntry, mSilentSectioner);
}
@Test
public void testClearableSilentSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mSbn.isClearable()).thenReturn(true);
- when(mRanking.isAmbient()).thenReturn(false);
+ setSbnClearable(true);
+ setRankingAmbient(false);
mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
}
@@ -238,17 +235,17 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Test
public void testClearableMinimizedSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mSbn.isClearable()).thenReturn(true);
- when(mRanking.isAmbient()).thenReturn(true);
+ setSbnClearable(true);
+ setRankingAmbient(true);
mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
}
@Test
public void testNotClearableSilentSection() {
- when(mSbn.isClearable()).thenReturn(false);
+ setSbnClearable(false);
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(false);
+ setRankingAmbient(false);
mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
@@ -257,9 +254,9 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Test
public void testNotClearableMinimizedSection() {
- when(mSbn.isClearable()).thenReturn(false);
+ setSbnClearable(false);
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(true);
+ setRankingAmbient(true);
mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
@@ -277,9 +274,24 @@ public class RankingCoordinatorTest extends SysuiTestCase {
}
private RankingBuilder getRankingForUnfilteredNotif() {
- return new RankingBuilder()
- .setKey(mEntry.getKey())
+ return new RankingBuilder(mEntry.getRanking())
.setSuppressedVisualEffects(0)
.setSuspended(false);
}
+
+ private void setSbnClearable(boolean clearable) {
+ mEntry.setSbn(new SbnBuilder(mEntry.getSbn())
+ .setFlag(mContext, Notification.FLAG_NO_CLEAR, !clearable)
+ .build());
+ assertEquals(clearable, mEntry.getSbn().isClearable());
+ }
+
+ private void setRankingAmbient(boolean ambient) {
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking())
+ .setImportance(ambient
+ ? NotificationManager.IMPORTANCE_MIN
+ : NotificationManager.IMPORTANCE_DEFAULT)
+ .build());
+ assertEquals(ambient, mEntry.getRanking().isAmbient());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 4edca7dd43d9..5df1d28073fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -19,8 +19,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,6 +32,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -37,7 +40,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -57,9 +59,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
private VisualStabilityCoordinator mCoordinator;
- // captured listeners and pluggables:
- private NotifCollectionListener mCollectionListener;
-
+ @Mock private DumpManager mDumpManager;
@Mock private NotifPipeline mNotifPipeline;
@Mock private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock private StatusBarStateController mStatusBarStateController;
@@ -69,7 +69,6 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
@Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
- @Captor private ArgumentCaptor<NotifCollectionListener> mNotifCollectionListenerCaptor;
private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -84,6 +83,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mCoordinator = new VisualStabilityCoordinator(
+ mDumpManager,
mHeadsUpManager,
mWakefulnessLifecycle,
mStatusBarStateController,
@@ -107,6 +107,12 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
.build();
when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(false);
+
+ // Whenever we invalidate, the pipeline runs again, so we invalidate the state
+ doAnswer(i -> {
+ mNotifStabilityManager.onBeginRun();
+ return null;
+ }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager));
}
@Test
@@ -211,7 +217,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis());
// THEN the notification list is invalidated
- verifyInvalidateCalled(true);
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -225,7 +231,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
// THEN invalidate is not called because this entry was never suppressed from reordering
- verifyInvalidateCalled(false);
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -241,7 +247,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN invalidate is not called because this entry was never suppressed from reordering;
// THEN section changes are allowed for this notification
- verifyInvalidateCalled(false);
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
// WHEN we're pulsing (now disallowing reordering)
@@ -268,13 +274,14 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// WHEN we temporarily allow section changes for this notification entry
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
- verifyInvalidateCalled(true); // can now reorder, so invalidates
+ // can now reorder, so invalidates
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
// WHEN reordering is now allowed because device isn't pulsing anymore
setPulsing(false);
- // THEN invalidate isn't called since reordering was already allowed
- verifyInvalidateCalled(false);
+ // THEN invalidate isn't called a second time since reordering was already allowed
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -292,7 +299,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN we never see any calls to invalidate since there weren't any notifications that
// were being suppressed from grouping or section changes
- verifyInvalidateCalled(false);
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -308,7 +315,41 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelExpanded(false);
// invalidate is called because we were previously suppressing a group change
- verifyInvalidateCalled(true);
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false);
+ setScreenOn(true);
+ setPanelExpanded(true);
+
+ assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+ // The pipeline still has to report back that entry reordering was suppressed
+ mNotifStabilityManager.onEntryReorderSuppressed();
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false);
+
+ // invalidate is called because we were previously suppressing an entry reorder
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false);
+ setScreenOn(true);
+ setPanelExpanded(true);
+
+ assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false);
+
+ // invalidate is not called because we were not told that an entry reorder was suppressed
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -345,13 +386,4 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mStatusBarStateListener.onExpandedChanged(expanded);
}
- private void verifyInvalidateCalled(boolean invalidateCalled) {
- if (invalidateCalled) {
- verify(mInvalidateListener).onPluggableInvalidated(mNotifStabilityManager);
- } else {
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
- }
-
- reset(mInvalidateListener);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index ed42ac3efe80..0d996025392f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -70,8 +70,8 @@ import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.icon.IconBuilder;
@@ -283,6 +283,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mRowBinder = new NotificationRowBinderImpl(
mContext,
+ mFeatureFlags,
new NotificationMessagingUtil(mContext),
mRemoteInputManager,
mLockscreenUserManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 5c0efd36fcd1..c9462d651bc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -101,6 +101,11 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
// Initial non-set value
when(mRingerModeLiveData.getValue()).thenReturn(-1);
when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
+ // Enable group volume adjustments
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions,
+ true);
+
mCallback = mock(VolumeDialogControllerImpl.C.class);
mThreadFactory.setLooper(TestableLooper.get(this).getLooper());
mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 214769bbd9c9..0e3932799123 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -272,6 +272,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return getUserStateLocked(mCurrentUserId);
}
+ /**
+ * Changes the magnification mode on the given display.
+ *
+ * @param displayId the logical display
+ * @param magnificationMode the target magnification mode
+ */
+ public void changeMagnificationMode(int displayId, int magnificationMode) {
+ synchronized (mLock) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ persistMagnificationModeSettingsLocked(magnificationMode);
+ } else {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ final int currentMode = userState.getMagnificationModeLocked(displayId);
+ if (magnificationMode != currentMode) {
+ userState.setMagnificationModeLocked(displayId, magnificationMode);
+ updateMagnificationModeChangeSettingsLocked(userState, displayId);
+ }
+ }
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private final AccessibilityManagerService mService;
@@ -299,7 +320,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
AccessibilityDisplayListener a11yDisplayListener,
- MagnificationController magnificationController) {
+ MagnificationController magnificationController,
+ @Nullable AccessibilityInputFilter inputFilter) {
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
@@ -314,6 +336,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mA11yDisplayListener = a11yDisplayListener;
mMagnificationController = magnificationController;
mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
+ if (inputFilter != null) {
+ mInputFilter = inputFilter;
+ mHasInputFilter = true;
+ }
init();
}
@@ -1794,47 +1820,51 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return relevantEventTypes;
}
- private void updateMagnificationModeChangeSettingsLocked(AccessibilityUserState userState) {
+ private void updateMagnificationModeChangeSettingsLocked(AccessibilityUserState userState,
+ int displayId) {
if (userState.mUserId != mCurrentUserId) {
return;
}
// New mode is invalid, so ignore and restore it.
- if (fallBackMagnificationModeSettingsLocked(userState)) {
+ if (fallBackMagnificationModeSettingsLocked(userState, displayId)) {
return;
}
mMagnificationController.transitionMagnificationModeLocked(
- Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
+ displayId, userState.getMagnificationModeLocked(displayId),
this::onMagnificationTransitionEndedLocked);
}
/**
- * Called when the magnification mode transition is completed.
+ * Called when the magnification mode transition is completed. If the given display is default
+ * display, we also need to fall back the mode in user settings.
*/
- void onMagnificationTransitionEndedLocked(boolean success) {
+ void onMagnificationTransitionEndedLocked(int displayId, boolean success) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
- final int previousMode = userState.getMagnificationModeLocked()
+ final int previousMode = userState.getMagnificationModeLocked(displayId)
^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
if (!success && previousMode != 0) {
- userState.setMagnificationModeLocked(previousMode);
- persistMagnificationModeSettingLocked(previousMode);
+ userState.setMagnificationModeLocked(displayId, previousMode);
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ persistMagnificationModeSettingsLocked(previousMode);
+ }
} else {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
- this));
+ this, displayId));
}
}
- private void notifyRefreshMagnificationModeToInputFilter() {
+ private void notifyRefreshMagnificationModeToInputFilter(int displayId) {
synchronized (mLock) {
if (!mHasInputFilter) {
return;
}
- // TODO: notify the mode change on specified display.
final ArrayList<Display> displays = getValidDisplayList();
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
- if (display != null) {
+ if (display != null && display.getDisplayId() == displayId) {
mInputFilter.refreshMagnificationMode(display);
+ return;
}
}
}
@@ -2245,12 +2275,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
scheduleUpdateClientsIfNeededLocked(userState);
updateAccessibilityShortcutKeyTargetsLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
- // Update the capabilities before the mode.
+ // Update the capabilities before the mode because we will check the current mode is
+ // invalid or not..
updateMagnificationCapabilitiesSettingsChangeLocked(userState);
- updateMagnificationModeChangeSettingsLocked(userState);
+ updateMagnificationModeChangeSettingsForAllDisplaysLocked(userState);
updateFocusAppearanceDataLocked(userState);
}
+ private void updateMagnificationModeChangeSettingsForAllDisplaysLocked(
+ AccessibilityUserState userState) {
+ final ArrayList<Display> displays = getValidDisplayList();
+ for (int i = 0; i < displays.size(); i++) {
+ final int displayId = displays.get(i).getDisplayId();
+ updateMagnificationModeChangeSettingsLocked(userState, displayId);
+ }
+ }
+
private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
// We observe windows for accessibility only if there is at least
// one bound service that can retrieve window content that specified
@@ -2348,7 +2388,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readAccessibilityButtonTargetsLocked(userState);
somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
- somethingChanged |= readMagnificationModeLocked(userState);
+ somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
somethingChanged |= readMagnificationCapabilitiesLocked(userState);
return somethingChanged;
}
@@ -3878,8 +3918,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| mUserInteractiveUiTimeoutUri.equals(uri)) {
readUserRecommendedUiTimeoutSettingsLocked(userState);
} else if (mMagnificationModeUri.equals(uri)) {
- if (readMagnificationModeLocked(userState)) {
- updateMagnificationModeChangeSettingsLocked(userState);
+ if (readMagnificationModeForDefaultDisplayLocked(userState)) {
+ updateMagnificationModeChangeSettingsLocked(userState,
+ Display.DEFAULT_DISPLAY);
}
} else if (mMagnificationCapabilityUri.equals(uri)) {
if (readMagnificationCapabilitiesLocked(userState)) {
@@ -3892,8 +3933,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void updateMagnificationCapabilitiesSettingsChangeLocked(
AccessibilityUserState userState) {
- if (fallBackMagnificationModeSettingsLocked(userState)) {
- updateMagnificationModeChangeSettingsLocked(userState);
+ final ArrayList<Display> displays = getValidDisplayList();
+ for (int i = 0; i < displays.size(); i++) {
+ final int displayId = displays.get(i).getDisplayId();
+ if (fallBackMagnificationModeSettingsLocked(userState, displayId)) {
+ updateMagnificationModeChangeSettingsLocked(userState, displayId);
+ }
}
updateWindowMagnificationConnectionIfNeeded(userState);
// Remove magnification button UI when the magnification capability is not all mode or
@@ -3902,7 +3947,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| userState.isShortcutMagnificationEnabledLocked())
|| userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
- final ArrayList<Display> displays = getValidDisplayList();
+
for (int i = 0; i < displays.size(); i++) {
final int displayId = displays.get(i).getDisplayId();
getWindowMagnificationMgr().removeMagnificationButton(displayId);
@@ -3910,18 +3955,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean fallBackMagnificationModeSettingsLocked(AccessibilityUserState userState) {
- if (userState.isValidMagnificationModeLocked()) {
+ private boolean fallBackMagnificationModeSettingsLocked(AccessibilityUserState userState,
+ int displayId) {
+ if (userState.isValidMagnificationModeLocked(displayId)) {
return false;
}
- Slog.w(LOG_TAG, "invalid magnification mode:" + userState.getMagnificationModeLocked());
+ Slog.w(LOG_TAG, "displayId " + displayId + ", invalid magnification mode:"
+ + userState.getMagnificationModeLocked(displayId));
final int capabilities = userState.getMagnificationCapabilitiesLocked();
- userState.setMagnificationModeLocked(capabilities);
- persistMagnificationModeSettingLocked(capabilities);
+ userState.setMagnificationModeLocked(displayId, capabilities);
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ persistMagnificationModeSettingsLocked(capabilities);
+ }
return true;
}
- private void persistMagnificationModeSettingLocked(int mode) {
+ private void persistMagnificationModeSettingsLocked(int mode) {
BackgroundThread.getHandler().post(() -> {
final long identity = Binder.clearCallingIdentity();
try {
@@ -3933,7 +3982,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
});
}
- //TODO: support multi-display.
/**
* Gets the magnification mode of the specified display.
*
@@ -3943,17 +3991,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
public int getMagnificationMode(int displayId) {
synchronized (mLock) {
- return getCurrentUserStateLocked().getMagnificationModeLocked();
+ return getCurrentUserStateLocked().getMagnificationModeLocked(displayId);
}
}
- private boolean readMagnificationModeLocked(AccessibilityUserState userState) {
+ // Only the value of the default display is from user settings because not each of displays has
+ // a unique id.
+ private boolean readMagnificationModeForDefaultDisplayLocked(AccessibilityUserState userState) {
final int magnificationMode = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, userState.mUserId);
- if (magnificationMode != userState.getMagnificationModeLocked()) {
- userState.setMagnificationModeLocked(magnificationMode);
+ if (magnificationMode != userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY)) {
+ userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY, magnificationMode);
return true;
}
return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index c70bf73fc7f5..8c3ca3430a3a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -23,6 +23,7 @@ import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
@@ -42,6 +43,7 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
@@ -122,8 +124,8 @@ class AccessibilityUserState {
/** {@code true} if the device config supports magnification area. */
private final boolean mSupportMagnificationArea;
- // The magnification mode of default display.
- private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ // The magnification modes on displays.
+ private final SparseIntArray mMagnificationModes = new SparseIntArray();
// The magnification capabilities used to know magnification mode could be switched.
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@@ -141,12 +143,13 @@ class AccessibilityUserState {
@SoftKeyboardShowMode
private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
- boolean isValidMagnificationModeLocked() {
+ boolean isValidMagnificationModeLocked(int displayId) {
+ final int mode = getMagnificationModeLocked(displayId);
if (!mSupportMagnificationArea
- && mMagnificationMode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ && mode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
return false;
}
- return (mMagnificationCapabilities & mMagnificationMode) != 0;
+ return (mMagnificationCapabilities & mode) != 0;
}
interface ServiceInfoChangeListener {
@@ -203,7 +206,7 @@ class AccessibilityUserState {
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
- mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ mMagnificationModes.clear();
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
}
@@ -500,7 +503,7 @@ class AccessibilityUserState {
pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
- pw.append(", magnificationMode=").append(String.valueOf(mMagnificationMode));
+ pw.append(", magnificationModes=").append(String.valueOf(mMagnificationModes));
pw.append(", magnificationCapabilities=")
.append(String.valueOf(mMagnificationCapabilities));
pw.append("}");
@@ -635,14 +638,19 @@ class AccessibilityUserState {
}
/**
- * Gets the magnification mode of default display.
+ * Gets the magnification mode for the given display.
* @return magnification mode
*
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
*/
- public int getMagnificationModeLocked() {
- return mMagnificationMode;
+ public int getMagnificationModeLocked(int displayId) {
+ int mode = mMagnificationModes.get(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
+ if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
+ mode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ setMagnificationModeLocked(displayId, mode);
+ }
+ return mode;
}
@@ -671,11 +679,13 @@ class AccessibilityUserState {
}
/**
- * Sets the magnification mode of default display.
+ * Sets the magnification mode to the given display.
+ *
+ * @param displayId The display id.
* @param mode The magnification mode.
*/
- public void setMagnificationModeLocked(int mode) {
- mMagnificationMode = mode;
+ public void setMagnificationModeLocked(int displayId, int mode) {
+ mMagnificationModes.put(displayId, mode);
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 935df9934dcb..966d887d11f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -208,8 +208,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
mPromptController.onDestroy();
// Check if need to reset when MagnificationGestureHandler is the last magnifying service.
- mFullScreenMagnificationController.resetAllIfNeeded(
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mFullScreenMagnificationController.resetIfNeeded(
+ mDisplayId, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
clearAndTransitionToStateDetecting();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 3708c7a422a0..6473bf5ffc3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -92,14 +92,16 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private long mFullScreenModeEnabledTime = 0;
/**
- * A callback to inform the magnification transition result.
+ * A callback to inform the magnification transition result on the given display.
*/
public interface TransitionCallBack {
/**
* Invoked when the transition ends.
+ *
+ * @param displayId The display id.
* @param success {@code true} if the transition success.
*/
- void onResult(boolean success);
+ void onResult(int displayId, boolean success);
}
public MagnificationController(AccessibilityManagerService ams, Object lock,
@@ -179,7 +181,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
final DisableMagnificationCallback animationCallback =
getDisableMagnificationEndRunnableLocked(displayId);
if (magnificationCenter == null && animationCallback == null) {
- transitionCallBack.onResult(true);
+ transitionCallBack.onResult(displayId, true);
return;
}
@@ -195,7 +197,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (magnificationCenter == null) {
Slog.w(TAG, "Invalid center, ignore it");
- transitionCallBack.onResult(true);
+ transitionCallBack.onResult(displayId, true);
return;
}
final FullScreenMagnificationController screenMagnificationController =
@@ -251,6 +253,11 @@ public class MagnificationController implements WindowMagnificationManager.Callb
updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
+ @Override
+ public void onChangeMagnificationMode(int displayId, int magnificationMode) {
+ mAms.changeMagnificationMode(displayId, magnificationMode);
+ }
+
private void disableFullScreenMagnificationIfNeeded(int displayId) {
final FullScreenMagnificationController fullScreenMagnificationController =
getFullScreenMagnificationController();
@@ -514,7 +521,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
applyMagnificationModeLocked(mTargetMode);
}
updateMagnificationButton(mDisplayId, mTargetMode);
- mTransitionCallBack.onResult(success);
+ mTransitionCallBack.onResult(mDisplayId, success);
}
}
@@ -539,7 +546,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
setExpiredAndRemoveFromListLocked();
applyMagnificationModeLocked(mCurrentMode);
updateMagnificationButton(mDisplayId, mCurrentMode);
- mTransitionCallBack.onResult(true);
+ mTransitionCallBack.onResult(mDisplayId, true);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index ce7ba7568b6e..bfaab9a0aa70 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -106,6 +106,15 @@ public class WindowMagnificationManager implements
* @param activated {@code true} if the magnification is activated, otherwise {@code false}.
*/
void onWindowMagnificationActivationState(int displayId, boolean activated);
+
+ /**
+ * Called from {@link IWindowMagnificationConnection} to request changing the magnification
+ * mode on the given display.
+ *
+ * @param displayId the logical display id
+ * @param magnificationMode the target magnification mode
+ */
+ void onChangeMagnificationMode(int displayId, int magnificationMode);
}
private final Callback mCallback;
@@ -535,7 +544,7 @@ public class WindowMagnificationManager implements
FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
"displayId=" + displayId + ";mode=" + magnificationMode);
}
- //TODO: Uses this method to change the magnification mode on non-default display.
+ mCallback.onChangeMagnificationMode(displayId, magnificationMode);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 1fd795128fad..2645f3f9d9f5 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -17,6 +17,7 @@
package com.android.server.companion;
import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -32,6 +33,7 @@ import static java.util.Collections.unmodifiableMap;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.role.RoleManager;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
@@ -41,8 +43,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.Signature;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.PackageUtils;
import android.util.Slog;
@@ -56,6 +60,7 @@ import com.android.server.FgThread;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -69,6 +74,8 @@ class AssociationRequestsProcessor {
map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
map.put(DEVICE_PROFILE_APP_STREAMING,
Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
+ map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION);
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
@@ -87,12 +94,14 @@ class AssociationRequestsProcessor {
private IFindDeviceCallback mFindDeviceCallback;
private String mCallingPackage;
private AndroidFuture<?> mOngoingDeviceDiscovery;
+ private RoleManager mRoleManager;
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
- AssociationRequestsProcessor(CompanionDeviceManagerService service) {
+ AssociationRequestsProcessor(CompanionDeviceManagerService service, RoleManager roleManager) {
mContext = service.getContext();
mService = service;
+ mRoleManager = roleManager;
final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -165,6 +174,16 @@ class AssociationRequestsProcessor {
}));
}
+ private boolean isRoleHolder(int userId, String packageName, String role) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<String> holders = mRoleManager.getRoleHoldersAsUser(role, UserHandle.of(userId));
+ return holders.contains(packageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) {
if (DEBUG) {
Slog.d(TAG, "stopScan(request = " + request + ")");
@@ -186,6 +205,12 @@ class AssociationRequestsProcessor {
"DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
}
+ if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
+ // TODO: remove, when properly supporting this profile.
+ throw new UnsupportedOperationException(
+ "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
+ }
+
if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
}
@@ -217,6 +242,12 @@ class AssociationRequestsProcessor {
}
private boolean mayAssociateWithoutPrompt(String packageName, int userId) {
+ if (mRequest.getDeviceProfile() != null
+ && isRoleHolder(userId, packageName, mRequest.getDeviceProfile())) {
+ // Don't need to collect user's consent since app already holds the role.
+ return true;
+ }
+
String[] sameOemPackages = mContext.getResources()
.getStringArray(com.android.internal.R.array.config_companionDevicePackages);
if (!ArrayUtils.contains(sameOemPackages, packageName)) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index cebabcb20402..d5357dc1dc5e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -200,7 +200,6 @@ public class CompanionDeviceManagerService extends SystemService {
super(context);
mImpl = new CompanionDeviceManagerImpl();
mPersistentDataStore = new PersistentDataStore();
- mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
mRoleManager = context.getSystemService(RoleManager.class);
@@ -213,6 +212,7 @@ public class CompanionDeviceManagerService extends SystemService {
context.getSystemService(PermissionControllerManager.class));
mUserManager = context.getSystemService(UserManager.class);
mCompanionDevicePresenceController = new CompanionDevicePresenceController();
+ mAssociationRequestsProcessor = new AssociationRequestsProcessor(this, mRoleManager);
registerPackageMonitor();
}
@@ -757,7 +757,12 @@ public class CompanionDeviceManagerService extends SystemService {
android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
mPowerWhitelistManager.addToWhitelist(packageInfo.packageName);
} else {
- mPowerWhitelistManager.removeFromWhitelist(packageInfo.packageName);
+ try {
+ mPowerWhitelistManager.removeFromWhitelist(packageInfo.packageName);
+ } catch (UnsupportedOperationException e) {
+ Slog.w(LOG_TAG, packageInfo.packageName + " can't be removed from power save"
+ + " whitelist. It might due to the package is whitelisted by the system.");
+ }
}
NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 16343e59bce9..9f22489f9305 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -149,9 +149,11 @@ java_library_static {
"android.hardware.boot-V1.1-java",
"android.hardware.boot-V1.2-java",
"android.hardware.broadcastradio-V2.0-java",
- "android.hardware.health-V1.0-java",
- "android.hardware.health-V2.0-java",
- "android.hardware.health-V2.1-java",
+ "android.hardware.health-V1.0-java", // HIDL
+ "android.hardware.health-V2.0-java", // HIDL
+ "android.hardware.health-V2.1-java", // HIDL
+ "android.hardware.health-V1-java", // AIDL
+ "android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
"android.hardware.weaver-V1.0-java",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 0146aa82a217..844ac86e8eb5 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.server.health.Utils.copyV1Battery;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -25,14 +26,8 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
-import android.hardware.health.V1_0.HealthInfo;
-import android.hardware.health.V2_0.IHealth;
-import android.hardware.health.V2_0.Result;
+import android.hardware.health.HealthInfo;
import android.hardware.health.V2_1.BatteryCapacityLevel;
-import android.hardware.health.V2_1.Constants;
-import android.hardware.health.V2_1.IHealthInfoCallback;
-import android.hidl.manager.V1_0.IServiceManager;
-import android.hidl.manager.V1_0.IServiceNotification;
import android.metrics.LogMaker;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -44,7 +39,6 @@ import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.OsProtoEnums;
@@ -62,15 +56,14 @@ import android.provider.Settings;
import android.service.battery.BatteryServiceDumpProto;
import android.sysprop.PowerProperties;
import android.util.EventLog;
-import android.util.MutableInt;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.health.HealthServiceWrapper;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -82,8 +75,6 @@ import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -147,7 +138,6 @@ public final class BatteryService extends SystemService {
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
- private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -191,7 +181,6 @@ public final class BatteryService extends SystemService {
private ActivityManagerInternal mActivityManagerInternal;
private HealthServiceWrapper mHealthServiceWrapper;
- private HealthHalCallback mHealthHalCallback;
private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
private long mLastBatteryLevelChangedSentMs;
@@ -274,13 +263,9 @@ public final class BatteryService extends SystemService {
private void registerHealthCallback() {
traceBegin("HealthInitWrapper");
- mHealthServiceWrapper = new HealthServiceWrapper();
- mHealthHalCallback = new HealthHalCallback();
// IHealth is lazily retrieved.
try {
- mHealthServiceWrapper.init(mHealthHalCallback,
- new HealthServiceWrapper.IServiceManagerSupplier() {},
- new HealthServiceWrapper.IHealthSupplier() {});
+ mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback. (RemoteException)");
throw ex.rethrowFromSystemServer();
@@ -368,8 +353,8 @@ public final class BatteryService extends SystemService {
}
private boolean shouldShutdownLocked() {
- if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
- return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+ if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+ return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
}
if (mHealthInfo.batteryLevel > 0) {
return false;
@@ -411,7 +396,7 @@ public final class BatteryService extends SystemService {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
- if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
+ if (mHealthInfo.batteryTemperatureTenthsCelsius > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -428,51 +413,28 @@ public final class BatteryService extends SystemService {
}
}
- private void update(android.hardware.health.V2_1.HealthInfo info) {
+ private void update(android.hardware.health.HealthInfo info) {
traceBegin("HealthInfoUpdate");
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
- info.legacy.legacy.batteryChargeCounter);
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
- info.legacy.legacy.batteryCurrent);
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType",
- plugType(info.legacy.legacy));
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus",
- info.legacy.legacy.batteryStatus);
+ Trace.traceCounter(
+ Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);
synchronized (mLock) {
if (!mUpdatesStopped) {
- mHealthInfo = info.legacy.legacy;
- mHealthInfo2p1 = info;
+ mHealthInfo = info;
// Process the new values.
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
- copy(mLastHealthInfo, info.legacy.legacy);
+ copyV1Battery(mLastHealthInfo, info);
}
}
traceEnd();
}
- private static void copy(HealthInfo dst, HealthInfo src) {
- dst.chargerAcOnline = src.chargerAcOnline;
- dst.chargerUsbOnline = src.chargerUsbOnline;
- dst.chargerWirelessOnline = src.chargerWirelessOnline;
- dst.maxChargingCurrent = src.maxChargingCurrent;
- dst.maxChargingVoltage = src.maxChargingVoltage;
- dst.batteryStatus = src.batteryStatus;
- dst.batteryHealth = src.batteryHealth;
- dst.batteryPresent = src.batteryPresent;
- dst.batteryLevel = src.batteryLevel;
- dst.batteryVoltage = src.batteryVoltage;
- dst.batteryTemperature = src.batteryTemperature;
- dst.batteryCurrent = src.batteryCurrent;
- dst.batteryCycleCount = src.batteryCycleCount;
- dst.batteryFullCharge = src.batteryFullCharge;
- dst.batteryChargeCounter = src.batteryChargeCounter;
- dst.batteryTechnology = src.batteryTechnology;
- }
-
private static int plugType(HealthInfo healthInfo) {
if (healthInfo.chargerAcOnline) {
return BatteryManager.BATTERY_PLUGGED_AC;
@@ -503,11 +465,16 @@ public final class BatteryService extends SystemService {
// Let the battery stats keep track of the current level.
try {
- mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
- mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
- mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
- mHealthInfo.batteryFullCharge,
- mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
+ mBatteryStats.setBatteryState(
+ mHealthInfo.batteryStatus,
+ mHealthInfo.batteryHealth,
+ mPlugType,
+ mHealthInfo.batteryLevel,
+ mHealthInfo.batteryTemperatureTenthsCelsius,
+ mHealthInfo.batteryVoltageMillivolts,
+ mHealthInfo.batteryChargeCounterUah,
+ mHealthInfo.batteryFullChargeUah,
+ mHealthInfo.batteryChargeTimeToFullNowSeconds);
} catch (RemoteException e) {
// Should never happen.
}
@@ -515,17 +482,18 @@ public final class BatteryService extends SystemService {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
- mHealthInfo.batteryHealth != mLastBatteryHealth ||
- mHealthInfo.batteryPresent != mLastBatteryPresent ||
- mHealthInfo.batteryLevel != mLastBatteryLevel ||
- mPlugType != mLastPlugType ||
- mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
- mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
- mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
- mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
- mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
- mInvalidCharger != mLastInvalidCharger)) {
+ if (force
+ || (mHealthInfo.batteryStatus != mLastBatteryStatus
+ || mHealthInfo.batteryHealth != mLastBatteryHealth
+ || mHealthInfo.batteryPresent != mLastBatteryPresent
+ || mHealthInfo.batteryLevel != mLastBatteryLevel
+ || mPlugType != mLastPlugType
+ || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
+ || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
+ || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
+ || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
+ || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
+ || mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -582,8 +550,11 @@ public final class BatteryService extends SystemService {
if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
- EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
- mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
+ EventLog.writeEvent(
+ EventLogTags.BATTERY_LEVEL,
+ mHealthInfo.batteryLevel,
+ mHealthInfo.batteryVoltageMillivolts,
+ mHealthInfo.batteryTemperatureTenthsCelsius);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
@@ -689,11 +660,11 @@ public final class BatteryService extends SystemService {
mLastBatteryPresent = mHealthInfo.batteryPresent;
mLastBatteryLevel = mHealthInfo.batteryLevel;
mLastPlugType = mPlugType;
- mLastBatteryVoltage = mHealthInfo.batteryVoltage;
- mLastBatteryTemperature = mHealthInfo.batteryTemperature;
- mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
- mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
- mLastChargeCounter = mHealthInfo.batteryChargeCounter;
+ mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
+ mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
+ mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
+ mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
+ mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -716,13 +687,17 @@ public final class BatteryService extends SystemService {
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
- intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
- intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ intent.putExtra(
+ BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
- intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ intent.putExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrentMicroamps);
+ intent.putExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE,
+ mHealthInfo.maxChargingVoltageMicrovolts);
+ intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+ ", info:" + mHealthInfo.toString());
@@ -742,9 +717,9 @@ public final class BatteryService extends SystemService {
event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
- event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
- event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
- event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
+ event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
@@ -936,7 +911,7 @@ public final class BatteryService extends SystemService {
}
try {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
boolean update = true;
switch (key) {
@@ -959,10 +934,10 @@ public final class BatteryService extends SystemService {
mHealthInfo.batteryLevel = Integer.parseInt(value);
break;
case "counter":
- mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
+ mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
break;
case "temp":
- mHealthInfo.batteryTemperature = Integer.parseInt(value);
+ mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
break;
case "invalid":
mInvalidCharger = Integer.parseInt(value);
@@ -1006,7 +981,7 @@ public final class BatteryService extends SystemService {
private void setChargerAcOnline(boolean online, boolean forceUpdate) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = online;
mUpdatesStopped = true;
@@ -1015,7 +990,7 @@ public final class BatteryService extends SystemService {
private void setBatteryLevel(int level, boolean forceUpdate) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.batteryLevel = level;
mUpdatesStopped = true;
@@ -1024,7 +999,7 @@ public final class BatteryService extends SystemService {
private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = false;
mHealthInfo.chargerUsbOnline = false;
@@ -1036,7 +1011,7 @@ public final class BatteryService extends SystemService {
private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
if (mUpdatesStopped) {
mUpdatesStopped = false;
- copy(mHealthInfo, mLastHealthInfo);
+ copyV1Battery(mHealthInfo, mLastHealthInfo);
Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
}
if (mBatteryInputSuspended) {
@@ -1071,16 +1046,16 @@ public final class BatteryService extends SystemService {
pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
- pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
- pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
- pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
+ pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
+ pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
+ pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah);
pw.println(" status: " + mHealthInfo.batteryStatus);
pw.println(" health: " + mHealthInfo.batteryHealth);
pw.println(" present: " + mHealthInfo.batteryPresent);
pw.println(" level: " + mHealthInfo.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mHealthInfo.batteryVoltage);
- pw.println(" temperature: " + mHealthInfo.batteryTemperature);
+ pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts);
+ pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
pw.println(" technology: " + mHealthInfo.batteryTechnology);
} else {
Shell shell = new Shell();
@@ -1103,16 +1078,23 @@ public final class BatteryService extends SystemService {
batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
}
proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
- proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ proto.write(
+ BatteryServiceDumpProto.MAX_CHARGING_CURRENT,
+ mHealthInfo.maxChargingCurrentMicroamps);
+ proto.write(
+ BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE,
+ mHealthInfo.maxChargingVoltageMicrovolts);
+ proto.write(
+ BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
- proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
- proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
+ proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ proto.write(
+ BatteryServiceDumpProto.TEMPERATURE,
+ mHealthInfo.batteryTemperatureTenthsCelsius);
proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
}
proto.flush();
@@ -1184,64 +1166,6 @@ public final class BatteryService extends SystemService {
}
}
- private final class HealthHalCallback extends IHealthInfoCallback.Stub
- implements HealthServiceWrapper.Callback {
- @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
- android.hardware.health.V2_1.HealthInfo propsLatest =
- new android.hardware.health.V2_1.HealthInfo();
- propsLatest.legacy = props;
-
- propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
- propsLatest.batteryChargeTimeToFullNowSeconds =
- Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
-
- BatteryService.this.update(propsLatest);
- }
-
- @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
- BatteryService.this.update(props);
- }
-
- // on new service registered
- @Override public void onRegistration(IHealth oldService, IHealth newService,
- String instance) {
- if (newService == null) return;
-
- traceBegin("HealthUnregisterCallback");
- try {
- if (oldService != null) {
- int r = oldService.unregisterCallback(this);
- if (r != Result.SUCCESS) {
- Slog.w(TAG, "health: cannot unregister previous callback: " +
- Result.toString(r));
- }
- }
- } catch (RemoteException ex) {
- Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
- + ex.getMessage());
- } finally {
- traceEnd();
- }
-
- traceBegin("HealthRegisterCallback");
- try {
- int r = newService.registerCallback(this);
- if (r != Result.SUCCESS) {
- Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
- return;
- }
- // registerCallback does NOT guarantee that update is called
- // immediately, so request a manual update here.
- newService.update();
- } catch (RemoteException ex) {
- Slog.e(TAG, "health: cannot register callback (transaction error): "
- + ex.getMessage());
- } finally {
- traceEnd();
- }
- }
- }
-
private final class BinderService extends Binder {
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1265,71 +1189,11 @@ public final class BatteryService extends SystemService {
private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
@Override
public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
- traceBegin("HealthGetProperty");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
- switch(id) {
- case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
- service.getChargeCounter((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
- service.getCurrentNow((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
- service.getCurrentAverage((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CAPACITY:
- service.getCapacity((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_STATUS:
- service.getChargeStatus((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
- service.getEnergyCounter((int result, long value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- }
- return outResult.value;
- } finally {
- traceEnd();
- }
+ return mHealthServiceWrapper.getProperty(id, prop);
}
@Override
public void scheduleUpdate() throws RemoteException {
- mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> {
- traceBegin("HealthScheduleUpdate");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) {
- Slog.e(TAG, "no health service");
- return;
- }
- service.update();
- } catch (RemoteException ex) {
- Slog.e(TAG, "Cannot call update on health HAL", ex);
- } finally {
- traceEnd();
- }
- });
+ mHealthServiceWrapper.scheduleUpdate();
}
}
@@ -1358,14 +1222,14 @@ public final class BatteryService extends SystemService {
@Override
public int getBatteryChargeCounter() {
synchronized (mLock) {
- return mHealthInfo.batteryChargeCounter;
+ return mHealthInfo.batteryChargeCounterUah;
}
}
@Override
public int getBatteryFullCharge() {
synchronized (mLock) {
- return mHealthInfo.batteryFullCharge;
+ return mHealthInfo.batteryFullChargeUah;
}
}
@@ -1418,184 +1282,4 @@ public final class BatteryService extends SystemService {
BatteryService.this.suspendBatteryInput();
}
}
-
- /**
- * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
- * necessary.
- *
- * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
- * the internal service is refreshed.
- * On death of an existing IHealth service, the internal service is NOT cleared to avoid
- * race condition between death notification and new service notification. Hence,
- * a caller must check for transaction errors when calling into the service.
- *
- * @hide Should only be used internally.
- */
- public static final class HealthServiceWrapper {
- private static final String TAG = "HealthServiceWrapper";
- public static final String INSTANCE_VENDOR = "default";
-
- private final IServiceNotification mNotification = new Notification();
- private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
- // These variables are fixed after init.
- private Callback mCallback;
- private IHealthSupplier mHealthSupplier;
- private String mInstanceName;
-
- // Last IHealth service received.
- private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
-
- /**
- * init should be called after constructor. For testing purposes, init is not called by
- * constructor.
- */
- public HealthServiceWrapper() {
- }
-
- public IHealth getLastService() {
- return mLastService.get();
- }
-
- /**
- * See {@link #init(Callback, IServiceManagerSupplier, IHealthSupplier)}
- */
- public void init() throws RemoteException, NoSuchElementException {
- init(/* callback= */null, new HealthServiceWrapper.IServiceManagerSupplier() {},
- new HealthServiceWrapper.IHealthSupplier() {});
- }
-
- /**
- * Start monitoring registration of new IHealth services. Only instance
- * {@link #INSTANCE_VENDOR} and in device / framework manifest are used. This function should
- * only be called once.
- *
- * mCallback.onRegistration() is called synchronously (aka in init thread) before
- * this method returns if callback is not null.
- *
- * @throws RemoteException transaction error when talking to IServiceManager
- * @throws NoSuchElementException if one of the following cases:
- * - No service manager;
- * - {@link #INSTANCE_VENDOR} is not in manifests (i.e. not
- * available on this device), or none of these instances are available to current
- * process.
- * @throws NullPointerException when supplier is null
- */
- void init(@Nullable Callback callback,
- IServiceManagerSupplier managerSupplier,
- IHealthSupplier healthSupplier)
- throws RemoteException, NoSuchElementException, NullPointerException {
- if (managerSupplier == null || healthSupplier == null) {
- throw new NullPointerException();
- }
- IServiceManager manager;
-
- mHealthSupplier = healthSupplier;
-
- // Initialize mLastService and call callback for the first time (in init thread)
- IHealth newService = null;
- traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
- try {
- newService = healthSupplier.get(INSTANCE_VENDOR);
- } catch (NoSuchElementException ex) {
- /* ignored, handled below */
- } finally {
- traceEnd();
- }
- if (newService != null) {
- mInstanceName = INSTANCE_VENDOR;
- mLastService.set(newService);
- }
-
- if (mInstanceName == null || newService == null) {
- throw new NoSuchElementException(String.format(
- "IHealth service instance %s isn't available. Perhaps no permission?",
- INSTANCE_VENDOR));
- }
-
- if (callback != null) {
- mCallback = callback;
- mCallback.onRegistration(null, newService, mInstanceName);
- }
-
- // Register for future service registrations
- traceBegin("HealthInitRegisterNotification");
- mHandlerThread.start();
- try {
- managerSupplier.get().registerForNotifications(
- IHealth.kInterfaceName, mInstanceName, mNotification);
- } finally {
- traceEnd();
- }
- Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
- }
-
- @VisibleForTesting
- HandlerThread getHandlerThread() {
- return mHandlerThread;
- }
-
- interface Callback {
- /**
- * This function is invoked asynchronously when a new and related IServiceNotification
- * is received.
- * @param service the recently retrieved service from IServiceManager.
- * Can be a dead service before service notification of a new service is delivered.
- * Implementation must handle cases for {@link RemoteException}s when calling
- * into service.
- * @param instance instance name.
- */
- void onRegistration(IHealth oldService, IHealth newService, String instance);
- }
-
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IServiceManagerSupplier {
- default IServiceManager get() throws NoSuchElementException, RemoteException {
- return IServiceManager.getService();
- }
- }
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IHealthSupplier {
- default IHealth get(String name) throws NoSuchElementException, RemoteException {
- return IHealth.getService(name, true /* retry */);
- }
- }
-
- private class Notification extends IServiceNotification.Stub {
- @Override
- public final void onRegistration(String interfaceName, String instanceName,
- boolean preexisting) {
- if (!IHealth.kInterfaceName.equals(interfaceName)) return;
- if (!mInstanceName.equals(instanceName)) return;
-
- // This runnable only runs on mHandlerThread and ordering is ensured, hence
- // no locking is needed inside the runnable.
- mHandlerThread.getThreadHandler().post(new Runnable() {
- @Override
- public void run() {
- try {
- IHealth newService = mHealthSupplier.get(mInstanceName);
- IHealth oldService = mLastService.getAndSet(newService);
-
- // preexisting may be inaccurate (race). Check for equality here.
- if (Objects.equals(newService, oldService)) return;
-
- Slog.i(TAG, "health: new instance registered " + mInstanceName);
- // #init() may be called with null callback. Skip null callbacks.
- if (mCallback == null) return;
- mCallback.onRegistration(oldService, newService, mInstanceName);
- } catch (NoSuchElementException | RemoteException ex) {
- Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
- + "': " + ex.getMessage() + ". Perhaps no permission?");
- }
- }
- });
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index cb6e73af56ac..c5b23790c918 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -19,12 +19,14 @@ package com.android.server;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Build;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
@@ -45,6 +47,9 @@ import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
/**
* Manages creating, starting, and other lifecycle events of
@@ -67,6 +72,19 @@ public final class SystemServiceManager implements Dumpable {
private static final String USER_STOPPING = "Stop"; // Logged as onStopUser
private static final String USER_STOPPED = "Cleanup"; // Logged as onCleanupUser
+ // Whether to use multiple threads to run user lifecycle phases in parallel.
+ private static boolean sUseLifecycleThreadPool = false;
+ // The default number of threads to use if lifecycle thread pool is enabled.
+ private static final int DEFAULT_MAX_USER_POOL_THREADS = 3;
+ // The number of threads to use if lifecycle thread pool is enabled, dependent on the number of
+ // available cores on the device.
+ private final int mNumUserPoolThreads;
+ // Maximum time to wait for a particular lifecycle phase to finish.
+ private static final long USER_POOL_SHUTDOWN_TIMEOUT_SECONDS = 30;
+ // Indirectly indicates how many services belong in the bootstrap and core service categories.
+ // This is used to decide which services the user lifecycle phases should be parallelized for.
+ private static volatile int sOtherServicesStartIndex;
+
private static File sSystemDir;
private final Context mContext;
private boolean mSafeMode;
@@ -100,6 +118,11 @@ public final class SystemServiceManager implements Dumpable {
SystemServiceManager(Context context) {
mContext = context;
+ // Disable using the thread pool for low ram devices
+ sUseLifecycleThreadPool = sUseLifecycleThreadPool
+ && !ActivityManager.isLowRamDeviceStatic();
+ mNumUserPoolThreads = Math.min(Runtime.getRuntime().availableProcessors(),
+ DEFAULT_MAX_USER_POOL_THREADS);
}
/**
@@ -261,6 +284,18 @@ public final class SystemServiceManager implements Dumpable {
}
/**
+ * Called from SystemServer to indicate that services in the other category are now starting.
+ * This is used to keep track of how many services are in the bootstrap and core service
+ * categories for the purposes of user lifecycle parallelization.
+ */
+ public void updateOtherServicesStartIndex() {
+ // Only update the index if the boot phase has not been completed yet
+ if (!isBootCompleted()) {
+ sOtherServicesStartIndex = mServices.size();
+ }
+ }
+
+ /**
* Called at the beginning of {@code ActivityManagerService.systemReady()}.
*/
public void preSystemReady() {
@@ -373,6 +408,13 @@ public final class SystemServiceManager implements Dumpable {
Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId
+ (prevUser != null ? " (from " + prevUser + ")" : ""));
final int serviceLen = mServices.size();
+ // Limit the lifecycle parallelization to all users other than the system user
+ // and only for the user start lifecycle phase for now.
+ final boolean useThreadPool = sUseLifecycleThreadPool
+ && curUserId != UserHandle.USER_SYSTEM
+ && onWhat.equals(USER_STARTING);
+ final ExecutorService threadPool =
+ useThreadPool ? Executors.newFixedThreadPool(mNumUserPoolThreads) : null;
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
final String serviceName = service.getClass().getName();
@@ -395,7 +437,11 @@ public final class SystemServiceManager implements Dumpable {
}
continue;
}
- t.traceBegin("ssm.on" + onWhat + "User-" + curUserId + "_" + serviceName);
+ // Only submit this service to the thread pool if it's in the "other" category.
+ final boolean submitToThreadPool = useThreadPool && i >= sOtherServicesStartIndex;
+ if (!submitToThreadPool) {
+ t.traceBegin("ssm.on" + onWhat + "User-" + curUserId + "_" + serviceName);
+ }
long time = SystemClock.elapsedRealtime();
try {
switch (onWhat) {
@@ -403,7 +449,11 @@ public final class SystemServiceManager implements Dumpable {
service.onUserSwitching(prevUser, curUser);
break;
case USER_STARTING:
- service.onUserStarting(curUser);
+ if (submitToThreadPool) {
+ threadPool.submit(getOnStartUserRunnable(t, service, curUser));
+ } else {
+ service.onUserStarting(curUser);
+ }
break;
case USER_UNLOCKING:
service.onUserUnlocking(curUser);
@@ -424,13 +474,54 @@ public final class SystemServiceManager implements Dumpable {
Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser
+ " to service " + serviceName, ex);
}
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
- "on" + onWhat + "User-" + curUserId);
- t.traceEnd(); // what on service
+ if (!submitToThreadPool) {
+ warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
+ "on" + onWhat + "User-" + curUserId);
+ t.traceEnd(); // what on service
+ }
+ }
+ if (useThreadPool) {
+ boolean terminated = false;
+ threadPool.shutdown();
+ try {
+ terminated = threadPool.awaitTermination(
+ USER_POOL_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Slog.wtf(TAG, "User lifecycle thread pool was interrupted while awaiting completion"
+ + " of " + onWhat + " of user " + curUser, e);
+ Slog.e(TAG, "Couldn't terminate, disabling thread pool. "
+ + "Please capture a bug report.");
+ sUseLifecycleThreadPool = false;
+ }
+ if (!terminated) {
+ Slog.wtf(TAG, "User lifecycle thread pool was not terminated.");
+ }
}
t.traceEnd(); // main entry
}
+ private Runnable getOnStartUserRunnable(TimingsTraceAndSlog oldTrace, SystemService service,
+ TargetUser curUser) {
+ return () -> {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog(oldTrace);
+ final String serviceName = service.getClass().getName();
+ try {
+ final int curUserId = curUser.getUserIdentifier();
+ t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName);
+ long time = SystemClock.elapsedRealtime();
+ service.onUserStarting(curUser);
+ warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
+ "on" + USER_STARTING + "User-" + curUserId);
+ t.traceEnd();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Failure reporting " + USER_STARTING + " of user " + curUser
+ + " to service " + serviceName, e);
+ Slog.e(TAG, "Disabling thread pool - please capture a bug report.");
+ sUseLifecycleThreadPool = false;
+ }
+ };
+ }
+
/** Sets the safe mode flag for services to query. */
void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1ce9f9b6ccb2..0eed1909944f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -32,7 +32,7 @@ import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_NONE;
@@ -444,6 +444,14 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final String SYSTEM_PROPERTY_DEVICE_PROVISIONED =
"persist.sys.device_provisioned";
+ /**
+ * Enabling this flag enforces the requirement for context registered receivers to use one of
+ * {@link Context#RECEIVER_EXPORTED} or {@link Context#RECEIVER_NOT_EXPORTED} for unprotected
+ * broadcasts
+ */
+ private static final boolean ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT =
+ SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", false);
+
static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
@@ -12646,28 +12654,43 @@ public class ActivityManagerService extends IActivityManager.Stub
// an error so the consumer can know to explicitly set the value for their flag.
// If the caller is registering for a sticky broadcast with a null receiver, we won't
// require a flag
- if (!onlyProtectedBroadcasts && receiver != null && (
- CompatChanges.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid)
- && (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED))
- == 0)) {
- Slog.e(TAG,
- callerPackage + ": Targeting T+ (version " + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a receiver");
- } else if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
+ final boolean explicitExportStateDefined =
+ (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
+ if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
(flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
throw new IllegalArgumentException(
"Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+ "flag");
}
+ if (CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
+ callingUid)
+ && !explicitExportStateDefined) {
+ if (ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT) {
+ throw new SecurityException(
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ } else {
+ Slog.wtf(TAG,
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ // Assume default behavior-- flag check is not enforced
+ flags |= Context.RECEIVER_EXPORTED;
+ }
+ } else if (!CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
+ callingUid)) {
+ // Change is not enabled, thus not targeting T+. Assume exported.
+ flags |= Context.RECEIVER_EXPORTED;
+ }
}
// Dynamic receivers are exported by default for versions prior to T
- final boolean exported =
- ((flags & Context.RECEIVER_EXPORTED) != 0
- || (!CompatChanges.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid)));
+ final boolean exported = (flags & Context.RECEIVER_EXPORTED) != 0;
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
@@ -15356,8 +15379,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
- mUserController.setStopBackgroundUsersOnSwitch(value);
+ public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
+ mUserController.setStopUserOnSwitch(value);
}
@Override
@@ -16685,8 +16708,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void setStopBackgroundUsersOnSwitch(int value) {
- ActivityManagerService.this.setStopBackgroundUsersOnSwitch(value);
+ public void setStopUserOnSwitch(int value) {
+ ActivityManagerService.this.setStopUserOnSwitch(value);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 25adddd0286b..5b33a710029d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -87,6 +87,7 @@ import android.os.ShellCommand;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -333,7 +334,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
case "get-isolated-pids":
return runGetIsolatedProcesses(pw);
case "set-stop-user-on-switch":
- return runSetStopBackgroundUsersOnSwitch(pw);
+ return runSetStopUserOnSwitch(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -1889,16 +1890,21 @@ final class ActivityManagerShellCommand extends ShellCommand {
int userId = Integer.parseInt(getNextArgRequired());
boolean switched;
- if (wait) {
- switched = switchUserAndWaitForComplete(userId);
- } else {
- switched = mInterface.switchUser(userId);
- }
- if (switched) {
- return 0;
- } else {
- pw.printf("Error: Failed to switch to user %d\n", userId);
- return 1;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runSwitchUser");
+ try {
+ if (wait) {
+ switched = switchUserAndWaitForComplete(userId);
+ } else {
+ switched = mInterface.switchUser(userId);
+ }
+ if (switched) {
+ return 0;
+ } else {
+ pw.printf("Error: Failed to switch to user %d\n", userId);
+ return 1;
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -3184,25 +3190,24 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
- private int runSetStopBackgroundUsersOnSwitch(PrintWriter pw) throws RemoteException {
+ private int runSetStopUserOnSwitch(PrintWriter pw) throws RemoteException {
mInternal.enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "setStopBackgroundUsersOnSwitch()");
+ "setStopUserOnSwitch()");
String arg = getNextArg();
if (arg == null) {
- Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): resetting to default value");
- mInternal.setStopBackgroundUsersOnSwitch(
- ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT);
+ Slogf.i(TAG, "setStopUserOnSwitch(): resetting to default value");
+ mInternal.setStopUserOnSwitch(ActivityManager.STOP_USER_ON_SWITCH_DEFAULT);
pw.println("Reset to default value");
return 0;
}
boolean stop = Boolean.parseBoolean(arg);
int value = stop
- ? ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE
- : ActivityManager.STOP_BG_USERS_ON_SWITCH_FALSE;
+ ? ActivityManager.STOP_USER_ON_SWITCH_TRUE
+ : ActivityManager.STOP_USER_ON_SWITCH_FALSE;
- Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): setting to %d (%b)", value, stop);
- mInternal.setStopBackgroundUsersOnSwitch(value);
+ Slogf.i(TAG, "runSetStopUserOnSwitch(): setting to %d (%b)", value, stop);
+ mInternal.setStopUserOnSwitch(value);
pw.println("Set to " + stop);
return 0;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8638c7da4da2..fd6f0994ecfb 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -792,7 +792,7 @@ public final class BroadcastQueue {
+ " due to receiver " + filter.receiverList.app
+ " (uid " + filter.receiverList.uid + ")"
+ " not specifying RECEIVER_EXPORTED");
- // skip = true;
+ skip = true;
}
if (skip) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index b5a9ee9adf77..c48ff9f9f2cc 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -97,6 +97,7 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
DeviceConfig.NAMESPACE_TETHERING,
+ DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0c518a0e988e..16d7f1d0ebd1 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -19,9 +19,9 @@ package com.android.server.am;
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT;
-import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_DEFAULT;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -376,7 +376,7 @@ class UserController implements Handler.Callback {
* user is switched.
*/
@GuardedBy("mLock")
- private @StopBgUsersOnSwitch int mStopBgUsersOnSwitch = STOP_BG_USERS_ON_SWITCH_DEFAULT;
+ private @StopUserOnSwitch int mStopUserOnSwitch = STOP_USER_ON_SWITCH_DEFAULT;
UserController(ActivityManagerService service) {
this(new Injector(service));
@@ -418,29 +418,27 @@ class UserController implements Handler.Callback {
}
}
- void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ void setStopUserOnSwitch(@StopUserOnSwitch int value) {
if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
== PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
== PackageManager.PERMISSION_DENIED) {
throw new SecurityException(
"You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
- + "call setStopBackgroundUsersOnSwitch()");
+ + "call setStopUserOnSwitch()");
}
synchronized (mLock) {
- Slogf.i(TAG, "setStopBackgroundUsersOnSwitch(): %d -> %d",
- mStopBgUsersOnSwitch, value);
- mStopBgUsersOnSwitch = value;
+ Slogf.i(TAG, "setStopUserOnSwitch(): %d -> %d", mStopUserOnSwitch, value);
+ mStopUserOnSwitch = value;
}
}
- private boolean shouldStopBackgroundUsersOnSwitch() {
+ private boolean shouldStopUserOnSwitch() {
synchronized (mLock) {
- if (mStopBgUsersOnSwitch != STOP_BG_USERS_ON_SWITCH_DEFAULT) {
- final boolean value = mStopBgUsersOnSwitch == STOP_BG_USERS_ON_SWITCH_TRUE;
- Slogf.i(TAG, "isStopBackgroundUsersOnSwitch(): returning overridden value (%b)",
- value);
+ if (mStopUserOnSwitch != STOP_USER_ON_SWITCH_DEFAULT) {
+ final boolean value = mStopUserOnSwitch == STOP_USER_ON_SWITCH_TRUE;
+ Slogf.i(TAG, "shouldStopUserOnSwitch(): returning overridden value (%b)", value);
return value;
}
}
@@ -1408,7 +1406,7 @@ class UserController implements Handler.Callback {
@Nullable IProgressListener unlockListener) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
+ t.traceBegin("UserController.startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
try {
return startUserInternal(userId, foreground, unlockListener, t);
} finally {
@@ -1846,7 +1844,7 @@ class UserController implements Handler.Callback {
mUserSwitchObservers.finishBroadcast();
}
- private void stopBackgroundUsersOnSwitchIfEnforced(@UserIdInt int oldUserId) {
+ private void stopUserOnSwitchIfEnforced(@UserIdInt int oldUserId) {
// Never stop system user
if (oldUserId == UserHandle.USER_SYSTEM) {
return;
@@ -1854,18 +1852,17 @@ class UserController implements Handler.Callback {
boolean hasRestriction =
hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, oldUserId);
synchronized (mLock) {
- // If running in background is disabled or mStopBackgroundUsersOnSwitch mode,
- // stop the user.
- boolean disallowRunInBg = hasRestriction || shouldStopBackgroundUsersOnSwitch();
+ // If running in background is disabled or mStopUserOnSwitch mode, stop the user.
+ boolean disallowRunInBg = hasRestriction || shouldStopUserOnSwitch();
if (!disallowRunInBg) {
if (DEBUG_MU) {
- Slogf.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related "
- + "users", oldUserId);
+ Slogf.i(TAG, "stopUserOnSwitchIfEnforced() NOT stopping %d and related users",
+ oldUserId);
}
return;
}
if (DEBUG_MU) {
- Slogf.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
+ Slogf.i(TAG, "stopUserOnSwitchIfEnforced() stopping %d and related users",
oldUserId);
}
stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
@@ -1979,7 +1976,7 @@ class UserController implements Handler.Callback {
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
stopGuestOrEphemeralUserIfBackground(oldUserId);
- stopBackgroundUsersOnSwitchIfEnforced(oldUserId);
+ stopUserOnSwitchIfEnforced(oldUserId);
t.traceEnd(); // end continueUserSwitch
}
@@ -2671,9 +2668,8 @@ class UserController implements Handler.Callback {
pw.println(" mTargetUserId:" + mTargetUserId);
pw.println(" mLastActiveUsers:" + mLastActiveUsers);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
- pw.println(" shouldStopBackgroundUsersOnSwitch():"
- + shouldStopBackgroundUsersOnSwitch());
- pw.println(" mStopBgUsersOnSwitch:" + mStopBgUsersOnSwitch);
+ pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
+ pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index ed494b569e87..47bd47ec20c8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2375,7 +2375,13 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
- if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
+ boolean doesCallerHavePermission = mContext.checkPermission(
+ android.Manifest.permission.GET_HISTORICAL_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid())
+ == PackageManager.PERMISSION_GRANTED;
+
+ if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController
+ && !doesCallerHavePermission) {
mHandler.post(() -> callback.sendResult(new Bundle()));
return;
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e0775d48b42f..f42870b4b734 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1381,7 +1381,8 @@ public class BiometricService extends SystemService {
Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first
+ "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
- + " requestId: " + requestId);
+ + " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
+ + promptInfo.isIgnoreEnrollmentState());
if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
// If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index cd0ff10168bb..a5a3542f49c7 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -83,6 +83,7 @@ class PreAuthInfo {
final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
final boolean credentialAvailable;
final boolean confirmationRequested;
+ final boolean ignoreEnrollmentState;
static PreAuthInfo create(ITrustManager trustManager,
DevicePolicyManager devicePolicyManager,
@@ -114,7 +115,8 @@ class PreAuthInfo {
@AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
checkDevicePolicyManager, requestedStrength,
- promptInfo.getAllowedSensorIds());
+ promptInfo.getAllowedSensorIds(),
+ promptInfo.isIgnoreEnrollmentState());
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -130,7 +132,8 @@ class PreAuthInfo {
}
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
- eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested);
+ eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
+ promptInfo.isIgnoreEnrollmentState());
}
/**
@@ -145,7 +148,8 @@ class PreAuthInfo {
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
boolean checkDevicePolicyManager, int requestedStrength,
- @NonNull List<Integer> requestedSensorIds) {
+ @NonNull List<Integer> requestedSensorIds,
+ boolean ignoreEnrollmentState) {
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
return BIOMETRIC_NO_HARDWARE;
@@ -167,7 +171,8 @@ class PreAuthInfo {
return BIOMETRIC_HARDWARE_NOT_DETECTED;
}
- if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)) {
+ if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)
+ && !ignoreEnrollmentState) {
return BIOMETRIC_NOT_ENROLLED;
}
@@ -238,7 +243,7 @@ class PreAuthInfo {
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
- boolean confirmationRequested) {
+ boolean confirmationRequested, boolean ignoreEnrollmentState) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
this.credentialRequested = credentialRequested;
@@ -247,6 +252,7 @@ class PreAuthInfo {
this.ineligibleSensors = ineligibleSensors;
this.credentialAvailable = credentialAvailable;
this.confirmationRequested = confirmationRequested;
+ this.ignoreEnrollmentState = ignoreEnrollmentState;
}
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index af8e8c2f2c79..3e70ee52ff1b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -281,7 +281,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public long authenticate(final IBinder token, final long operationId,
final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
- final String opPackageName) {
+ final String opPackageName, boolean ignoreEnrollmentState) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -333,7 +333,8 @@ public class FingerprintService extends SystemService {
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
final long identity2 = Binder.clearCallingIdentity();
try {
- return authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+ return authenticateWithPrompt(operationId, sensorProps, userId, receiver,
+ ignoreEnrollmentState);
} finally {
Binder.restoreCallingIdentity(identity2);
}
@@ -347,7 +348,8 @@ public class FingerprintService extends SystemService {
final long operationId,
@NonNull final FingerprintSensorPropertiesInternal props,
final int userId,
- final IFingerprintServiceReceiver receiver) {
+ final IFingerprintServiceReceiver receiver,
+ boolean ignoreEnrollmentState) {
final Context context = getUiContext();
final Executor executor = context.getMainExecutor();
@@ -368,6 +370,7 @@ public class FingerprintService extends SystemService {
})
.setAllowedSensorIds(new ArrayList<>(
Collections.singletonList(props.sensorId)))
+ .setIgnoreEnrollmentState(ignoreEnrollmentState)
.build();
final BiometricPrompt.AuthenticationCallback promptCallback =
diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS
index cfd0a4b079ad..ee3086ab2fdb 100644
--- a/services/core/java/com/android/server/compat/OWNERS
+++ b/services/core/java/com/android/server/compat/OWNERS
@@ -1,6 +1 @@
-# Use this reviewer by default.
-platform-compat-eng+reviews@google.com
-
-andreionea@google.com
-mathewi@google.com
-satayev@google.com
+include tools/platform-compat:/OWNERS
diff --git a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
new file mode 100644
index 000000000000..7a6698085c0d
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import static android.hardware.health.Translate.h2aTranslate;
+
+import android.annotation.NonNull;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_1.BatteryCapacityLevel;
+import android.hardware.health.V2_1.Constants;
+import android.hardware.health.V2_1.IHealthInfoCallback;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * On service registration, {@link HealthServiceWrapperHidl.Callback#onRegistration} is called,
+ * which registers {@code this}, a {@link IHealthInfoCallback}, to the health service.
+ *
+ * <p>When the health service has updates to health info, {@link HealthInfoCallback#update} is
+ * called.
+ *
+ * @hide
+ */
+class HealthHalCallbackHidl extends IHealthInfoCallback.Stub
+ implements HealthServiceWrapperHidl.Callback {
+
+ private static final String TAG = HealthHalCallbackHidl.class.getSimpleName();
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private HealthInfoCallback mCallback;
+
+ HealthHalCallbackHidl(@NonNull HealthInfoCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
+ android.hardware.health.V2_1.HealthInfo propsLatest =
+ new android.hardware.health.V2_1.HealthInfo();
+ propsLatest.legacy = props;
+
+ propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
+ propsLatest.batteryChargeTimeToFullNowSeconds =
+ Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+
+ mCallback.update(h2aTranslate(propsLatest));
+ }
+
+ @Override
+ public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
+ mCallback.update(h2aTranslate(props));
+ }
+
+ // on new service registered
+ @Override
+ public void onRegistration(IHealth oldService, IHealth newService, String instance) {
+ if (newService == null) return;
+
+ traceBegin("HealthUnregisterCallback");
+ try {
+ if (oldService != null) {
+ int r = oldService.unregisterCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback: " + Result.toString(r));
+ }
+ }
+ } catch (RemoteException ex) {
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback (transaction error): "
+ + ex.getMessage());
+ } finally {
+ traceEnd();
+ }
+
+ traceBegin("HealthRegisterCallback");
+ try {
+ int r = newService.registerCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
+ return;
+ }
+ // registerCallback does NOT guarantee that update is called
+ // immediately, so request a manual update here.
+ newService.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "health: cannot register callback (transaction error): " + ex.getMessage());
+ } finally {
+ traceEnd();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthInfoCallback.java b/services/core/java/com/android/server/health/HealthInfoCallback.java
new file mode 100644
index 000000000000..c2a77fc862fa
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthInfoCallback.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+/**
+ * A wrapper over HIDL / AIDL IHealthInfoCallback.
+ *
+ * @hide
+ */
+public interface HealthInfoCallback {
+ /**
+ * Signals to the client that health info is changed.
+ *
+ * @param props the new health info.
+ */
+ void update(android.hardware.health.HealthInfo props);
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
new file mode 100644
index 000000000000..9b97554ee259
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryProperty;
+import android.os.HandlerThread;
+import android.os.IBatteryPropertiesRegistrar;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+
+/**
+ * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when necessary.
+ * This is essentially a wrapper over IHealth that is useful for BatteryService.
+ *
+ * <p>The implementation may be backed by a HIDL or AIDL HAL.
+ *
+ * <p>On new registration of IHealth service, the internal service is refreshed. On death of an
+ * existing IHealth service, the internal service is NOT cleared to avoid race condition between
+ * death notification and new service notification. Hence, a caller must check for transaction
+ * errors when calling into the service.
+ *
+ * @hide Should only be used internally.
+ */
+public abstract class HealthServiceWrapper {
+ /** @return the handler thread. Exposed for testing. */
+ @VisibleForTesting
+ abstract HandlerThread getHandlerThread();
+
+ /**
+ * Calls into get*() functions in the health HAL. This reads into the kernel interfaces
+ * directly.
+ *
+ * @see IBatteryPropertiesRegistrar#getProperty
+ */
+ public abstract int getProperty(int id, BatteryProperty prop) throws RemoteException;
+
+ /**
+ * Calls update() in the health HAL.
+ *
+ * @see IBatteryPropertiesRegistrar#scheduleUpdate
+ */
+ public abstract void scheduleUpdate() throws RemoteException;
+
+ /**
+ * Calls into getHealthInfo() in the health HAL. This returns a cached value in the health HAL
+ * implementation.
+ *
+ * @return health info. {@code null} if no health HAL service. {@code null} if any
+ * service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported.
+ * @throws RemoteException for any transaction-level errors
+ */
+ public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException;
+
+ /**
+ * Create a new HealthServiceWrapper instance.
+ *
+ * @param healthInfoCallback the callback to call when health info changes
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ * @throws RemoteException transaction errors
+ * @throws NoSuchElementException no HIDL or AIDL service is available
+ */
+ public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback)
+ throws RemoteException, NoSuchElementException {
+ return create(
+ healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback),
+ new HealthServiceWrapperHidl.IServiceManagerSupplier() {},
+ new HealthServiceWrapperHidl.IHealthSupplier() {});
+ }
+
+ /**
+ * Create a new HealthServiceWrapper instance for testing.
+ *
+ * @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client
+ * does not care about HIDL service registration notifications
+ * @param hidlServiceManagerSupplier supplier of HIDL service manager
+ * @param hidlHealthSupplier supplier of HIDL health HAL
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ */
+ @VisibleForTesting
+ static @NonNull HealthServiceWrapper create(
+ @Nullable HealthServiceWrapperHidl.Callback hidlRegCallback,
+ @NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier,
+ @NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier)
+ throws RemoteException, NoSuchElementException {
+ return new HealthServiceWrapperHidl(
+ hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier);
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
new file mode 100644
index 000000000000..0301174a45c6
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+import static android.hardware.health.Translate.h2aTranslate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.MutableInt;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implement {@link HealthServiceWrapper} backed by the HIDL HAL.
+ *
+ * @hide
+ */
+final class HealthServiceWrapperHidl extends HealthServiceWrapper {
+ private static final String TAG = "HealthServiceWrapperHidl";
+ public static final String INSTANCE_VENDOR = "default";
+
+ private final IServiceNotification mNotification = new Notification();
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
+ // These variables are fixed after init.
+ private Callback mCallback;
+ private IHealthSupplier mHealthSupplier;
+ private String mInstanceName;
+
+ // Last IHealth service received.
+ private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ @Override
+ public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
+ traceBegin("HealthGetProperty");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) throw new RemoteException("no health service");
+ final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
+ switch (id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ service.getChargeCounter(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ service.getCurrentNow(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ service.getCurrentAverage(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ service.getCapacity(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ service.getChargeStatus(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ service.getEnergyCounter(
+ (int result, long value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ }
+ return outResult.value;
+ } finally {
+ traceEnd();
+ }
+ }
+
+ @Override
+ public void scheduleUpdate() throws RemoteException {
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) {
+ Slog.e(TAG, "no health service");
+ return;
+ }
+ service.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Cannot call update on health HAL", ex);
+ } finally {
+ traceEnd();
+ }
+ });
+ }
+
+ private static class Mutable<T> {
+ public T value;
+ }
+
+ @Override
+ public HealthInfo getHealthInfo() throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return null;
+ final Mutable<HealthInfo> ret = new Mutable<>();
+ service.getHealthInfo(
+ (result, value) -> {
+ if (result == Result.SUCCESS) {
+ ret.value = h2aTranslate(value.legacy);
+ }
+ });
+ return ret.value;
+ }
+
+ /**
+ * Start monitoring registration of new IHealth services. Only instance {@link #INSTANCE_VENDOR}
+ * and in device / framework manifest are used. This function should only be called once.
+ *
+ * <p>mCallback.onRegistration() is called synchronously (aka in init thread) before this method
+ * returns if callback is not null.
+ *
+ * @throws RemoteException transaction error when talking to IServiceManager
+ * @throws NoSuchElementException if one of the following cases: - No service manager; - {@link
+ * #INSTANCE_VENDOR} is not in manifests (i.e. not available on this device), or none of
+ * these instances are available to current process.
+ * @throws NullPointerException when supplier is null
+ */
+ @VisibleForTesting
+ HealthServiceWrapperHidl(
+ @Nullable Callback callback,
+ @NonNull IServiceManagerSupplier managerSupplier,
+ @NonNull IHealthSupplier healthSupplier)
+ throws RemoteException, NoSuchElementException, NullPointerException {
+ if (managerSupplier == null || healthSupplier == null) {
+ throw new NullPointerException();
+ }
+ mHealthSupplier = healthSupplier;
+
+ // Initialize mLastService and call callback for the first time (in init thread)
+ IHealth newService = null;
+ traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
+ try {
+ newService = healthSupplier.get(INSTANCE_VENDOR);
+ } catch (NoSuchElementException ex) {
+ /* ignored, handled below */
+ } finally {
+ traceEnd();
+ }
+ if (newService != null) {
+ mInstanceName = INSTANCE_VENDOR;
+ mLastService.set(newService);
+ }
+
+ if (mInstanceName == null || newService == null) {
+ throw new NoSuchElementException(
+ String.format(
+ "IHealth service instance %s isn't available. Perhaps no permission?",
+ INSTANCE_VENDOR));
+ }
+
+ if (callback != null) {
+ mCallback = callback;
+ mCallback.onRegistration(null, newService, mInstanceName);
+ }
+
+ // Register for future service registrations
+ traceBegin("HealthInitRegisterNotification");
+ mHandlerThread.start();
+ try {
+ managerSupplier
+ .get()
+ .registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
+ } finally {
+ traceEnd();
+ }
+ Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
+ }
+
+ @VisibleForTesting
+ public HandlerThread getHandlerThread() {
+ return mHandlerThread;
+ }
+
+ /** Service registration callback. */
+ interface Callback {
+ /**
+ * This function is invoked asynchronously when a new and related IServiceNotification is
+ * received.
+ *
+ * @param service the recently retrieved service from IServiceManager. Can be a dead service
+ * before service notification of a new service is delivered. Implementation must handle
+ * cases for {@link RemoteException}s when calling into service.
+ * @param instance instance name.
+ */
+ void onRegistration(IHealth oldService, IHealth newService, String instance);
+ }
+
+ /**
+ * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service
+ * is not available.
+ */
+ interface IServiceManagerSupplier {
+ default IServiceManager get() throws NoSuchElementException, RemoteException {
+ return IServiceManager.getService();
+ }
+ }
+
+ /**
+ * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service
+ * is not available.
+ */
+ interface IHealthSupplier {
+ default IHealth get(String name) throws NoSuchElementException, RemoteException {
+ return IHealth.getService(name, true /* retry */);
+ }
+ }
+
+ private class Notification extends IServiceNotification.Stub {
+ @Override
+ public final void onRegistration(
+ String interfaceName, String instanceName, boolean preexisting) {
+ if (!IHealth.kInterfaceName.equals(interfaceName)) return;
+ if (!mInstanceName.equals(instanceName)) return;
+
+ // This runnable only runs on mHandlerThread and ordering is ensured, hence
+ // no locking is needed inside the runnable.
+ mHandlerThread
+ .getThreadHandler()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ IHealth newService = mHealthSupplier.get(mInstanceName);
+ IHealth oldService = mLastService.getAndSet(newService);
+
+ // preexisting may be inaccurate (race). Check for equality
+ // here.
+ if (Objects.equals(newService, oldService)) return;
+
+ Slog.i(
+ TAG,
+ "health: new instance registered " + mInstanceName);
+ // #init() may be called with null callback. Skip null
+ // callbacks.
+ if (mCallback == null) return;
+ mCallback.onRegistration(
+ oldService, newService, mInstanceName);
+ } catch (NoSuchElementException | RemoteException ex) {
+ Slog.e(
+ TAG,
+ "health: Cannot get instance '"
+ + mInstanceName
+ + "': "
+ + ex.getMessage()
+ + ". Perhaps no permission?");
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/Utils.java b/services/core/java/com/android/server/health/Utils.java
new file mode 100644
index 000000000000..a8c978c50e42
--- /dev/null
+++ b/services/core/java/com/android/server/health/Utils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.health;
+
+/**
+ * Utils for {@link om.android.server.BatteryService} to deal with health info structs.
+ *
+ * @hide
+ */
+public class Utils {
+ private Utils() {}
+
+ /**
+ * Copy health info struct.
+ *
+ * @param dst destination
+ * @param src source
+ */
+ public static void copy(
+ android.hardware.health.V1_0.HealthInfo dst,
+ android.hardware.health.V1_0.HealthInfo src) {
+ dst.chargerAcOnline = src.chargerAcOnline;
+ dst.chargerUsbOnline = src.chargerUsbOnline;
+ dst.chargerWirelessOnline = src.chargerWirelessOnline;
+ dst.maxChargingCurrent = src.maxChargingCurrent;
+ dst.maxChargingVoltage = src.maxChargingVoltage;
+ dst.batteryStatus = src.batteryStatus;
+ dst.batteryHealth = src.batteryHealth;
+ dst.batteryPresent = src.batteryPresent;
+ dst.batteryLevel = src.batteryLevel;
+ dst.batteryVoltage = src.batteryVoltage;
+ dst.batteryTemperature = src.batteryTemperature;
+ dst.batteryCurrent = src.batteryCurrent;
+ dst.batteryCycleCount = src.batteryCycleCount;
+ dst.batteryFullCharge = src.batteryFullCharge;
+ dst.batteryChargeCounter = src.batteryChargeCounter;
+ dst.batteryTechnology = src.batteryTechnology;
+ }
+
+ /**
+ * Copy battery fields of {@link android.hardware.health.HealthInfo} V1. This excludes
+ * non-battery fields like {@link android.hardware.health.HealthInfo#diskStats diskStats} and
+ * {@link android.hardware.health.HealthInfo#storageInfos storageInfos}
+ *
+ * @param dst destination
+ * @param src source
+ */
+ public static void copyV1Battery(
+ android.hardware.health.HealthInfo dst, android.hardware.health.HealthInfo src) {
+ dst.chargerAcOnline = src.chargerAcOnline;
+ dst.chargerUsbOnline = src.chargerUsbOnline;
+ dst.chargerWirelessOnline = src.chargerWirelessOnline;
+ dst.maxChargingCurrentMicroamps = src.maxChargingCurrentMicroamps;
+ dst.maxChargingVoltageMicrovolts = src.maxChargingVoltageMicrovolts;
+ dst.batteryStatus = src.batteryStatus;
+ dst.batteryHealth = src.batteryHealth;
+ dst.batteryPresent = src.batteryPresent;
+ dst.batteryLevel = src.batteryLevel;
+ dst.batteryVoltageMillivolts = src.batteryVoltageMillivolts;
+ dst.batteryTemperatureTenthsCelsius = src.batteryTemperatureTenthsCelsius;
+ dst.batteryCurrentMicroamps = src.batteryCurrentMicroamps;
+ dst.batteryCycleCount = src.batteryCycleCount;
+ dst.batteryFullChargeUah = src.batteryFullChargeUah;
+ dst.batteryChargeCounterUah = src.batteryChargeCounterUah;
+ dst.batteryTechnology = src.batteryTechnology;
+ dst.batteryCurrentAverageMicroamps = src.batteryCurrentAverageMicroamps;
+ dst.batteryCapacityLevel = src.batteryCapacityLevel;
+ dst.batteryChargeTimeToFullNowSeconds = src.batteryChargeTimeToFullNowSeconds;
+ dst.batteryFullChargeDesignCapacityUah = src.batteryFullChargeDesignCapacityUah;
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d14ef162de3c..619071da43c5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1827,7 +1827,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiverAsUser(new ImmsBroadcastReceiverForAllUsers(),
UserHandle.ALL, broadcastFilterForAllUsers, null, null,
- Context.RECEIVER_NOT_EXPORTED);
+ Context.RECEIVER_EXPORTED);
final String defaultImiId = mSettings.getSelectedInputMethod();
final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
diff --git a/services/core/java/com/android/server/locales/TEST_MAPPING b/services/core/java/com/android/server/locales/TEST_MAPPING
index 72b9c484cd8b..097c2bc262e1 100644
--- a/services/core/java/com/android/server/locales/TEST_MAPPING
+++ b/services/core/java/com/android/server/locales/TEST_MAPPING
@@ -7,6 +7,9 @@
"include-filter": "com.android.server.locales."
}
]
+ },
+ {
+ "name": "CtsLocaleManagerTestCases"
}
]
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 607218e20ea8..b424c2083bd4 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -146,6 +146,12 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
}
@Override
+ public boolean canHandleVolumeKey() {
+ // TODO: Implement when MediaSession2 starts to get key events.
+ return false;
+ }
+
+ @Override
public int getSessionPolicies() {
synchronized (mLock) {
return mPolicies;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1525cd4da669..e4ed0e5d4186 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -26,7 +26,9 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.MediaMetadata;
+import android.media.MediaRouter2Manager;
import android.media.Rating;
+import android.media.RoutingSessionInfo;
import android.media.VolumeProvider;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -50,6 +52,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -121,6 +124,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private final SessionCb mSessionCb;
private final MediaSessionService mService;
private final Context mContext;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private final Object mLock = new Object();
private final CopyOnWriteArrayList<ISessionControllerCallbackHolder>
@@ -180,6 +184,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioAttrs = DEFAULT_ATTRIBUTES;
mPolicies = policies;
+ mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
// May throw RemoteException if the session app is killed.
mSessionCb.mCb.asBinder().linkToDeath(this, 0);
@@ -449,6 +455,33 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
@Override
+ public boolean canHandleVolumeKey() {
+ if (isPlaybackTypeLocal() || mVolumeAdjustmentForRemoteGroupSessions) {
+ return true;
+ }
+ MediaRouter2Manager mRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+ List<RoutingSessionInfo> sessions =
+ mRouter2Manager.getRoutingSessions(mPackageName);
+ boolean foundNonSystemSession = false;
+ boolean isGroup = false;
+ for (RoutingSessionInfo session : sessions) {
+ if (!session.isSystemSession()) {
+ foundNonSystemSession = true;
+ int selectedRouteCount = session.getSelectedRoutes().size();
+ if (selectedRouteCount > 1) {
+ isGroup = true;
+ break;
+ }
+ }
+ }
+ if (!foundNonSystemSession) {
+ Log.d(TAG, "No routing session for " + mPackageName);
+ return false;
+ }
+ return !isGroup;
+ }
+
+ @Override
public int getSessionPolicies() {
synchronized (mLock) {
return mPolicies;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 3c50597b8cfc..8f01f02f2ab1 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -131,6 +131,13 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
KeyEvent ke, int sequenceId, ResultReceiver cb);
/**
+ * Returns whether the media session can handle volume key events.
+ *
+ * @return True if this media session can handle volume key events, false otherwise.
+ */
+ boolean canHandleVolumeKey();
+
+ /**
* Get session policies from custom policy provider set when MediaSessionRecord is instantiated.
* If custom policy does not exist, will return null.
*/
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index c4c21df746b3..b75ba75e028b 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -325,8 +325,7 @@ class MediaSessionStack {
int size = records.size();
for (int i = 0; i < size; i++) {
MediaSessionRecord record = records.get(i);
- // Do not send the volume key events to remote sessions.
- if (record.checkPlaybackActiveState(true) && record.isPlaybackTypeLocal()) {
+ if (record.checkPlaybackActiveState(true) && record.canHandleVolumeKey()) {
mCachedVolumeDefault = record;
return record;
}
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 28ae6a417bd3..a15fc3eef539 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,11 +1,7 @@
set noparent
-codewiz@google.com
-jchalard@google.com
+include platform/packages/modules/Connectivity:/OWNERS
+
jsharkey@android.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
sudheersai@google.com
yamasani@google.com
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index d04aac2f5717..471c9b97200f 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -32,6 +32,7 @@ import android.util.Log;
import android.util.Slog;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.PackageManagerService;
import java.io.PrintWriter;
@@ -114,11 +115,7 @@ public class CountdownConditionProvider extends SystemConditionProviderService {
mIsAlarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
final AlarmManager alarms = (AlarmManager)
mContext.getSystemService(Context.ALARM_SERVICE);
- final Intent intent = new Intent(ACTION)
- .putExtra(EXTRA_CONDITION_ID, conditionId)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
- intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ final PendingIntent pendingIntent = getPendingIntent(conditionId);
alarms.cancel(pendingIntent);
if (mTime > 0) {
final long now = System.currentTimeMillis();
@@ -138,6 +135,16 @@ public class CountdownConditionProvider extends SystemConditionProviderService {
}
}
+ PendingIntent getPendingIntent(Uri conditionId) {
+ final Intent intent = new Intent(ACTION)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .putExtra(EXTRA_CONDITION_ID, conditionId)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
+ intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ return pendingIntent;
+ }
+
@Override
public void onUnsubscribe(Uri conditionId) {
// noop
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 25ad9280fa99..4be4f0a1e7f0 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -41,6 +41,7 @@ import android.util.SparseArray;
import com.android.server.notification.CalendarTracker.CheckEventResult;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.PackageManagerService;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -266,12 +267,7 @@ public class EventConditionProvider extends SystemConditionProviderService {
private void rescheduleAlarm(long now, long time) {
mNextAlarmTime = time;
final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_EVALUATE,
- new Intent(ACTION_EVALUATE)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_TIME, time),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ final PendingIntent pendingIntent = getPendingIntent(time);
alarms.cancel(pendingIntent);
if (time == 0 || time < now) {
if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified"
@@ -283,6 +279,17 @@ public class EventConditionProvider extends SystemConditionProviderService {
alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
}
+ PendingIntent getPendingIntent(long time) {
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_EVALUATE,
+ new Intent(ACTION_EVALUATE)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .putExtra(EXTRA_TIME, time),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ return pendingIntent;
+ }
+
private Condition createCondition(Uri id, int state) {
final String summary = NOT_SHOWN;
final String line1 = NOT_SHOWN;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5516109253df..6b7500ae4b2f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6784,11 +6784,13 @@ public class NotificationManagerService extends SystemService {
// blocked apps
+ boolean isMediaNotification = n.isMediaNotification()
+ && n.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
synchronized (mNotificationLock) {
isBlocked |= isRecordBlockedLocked(r);
}
- if (isBlocked) {
+ if (isBlocked && !isMediaNotification) {
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+ " by user request.");
@@ -7174,7 +7176,13 @@ public class NotificationManagerService extends SystemService {
return;
}
- if (appBanned || isRecordBlockedLocked(r)) {
+ final StatusBarNotification n = r.getSbn();
+ final Notification notification = n.getNotification();
+
+ boolean isMediaNotification = notification.isMediaNotification()
+ && notification.extras.getParcelable(
+ Notification.EXTRA_MEDIA_SESSION) != null;
+ if (!isMediaNotification && (appBanned || isRecordBlockedLocked(r))) {
mUsageStats.registerBlocked(r);
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + pkg);
@@ -7189,8 +7197,6 @@ public class NotificationManagerService extends SystemService {
mUsageStats.registerSuspendedByAdmin(r);
}
NotificationRecord old = mNotificationsByKey.get(key);
- final StatusBarNotification n = r.getSbn();
- final Notification notification = n.getNotification();
// Make sure the SBN has an instance ID for statsd logging.
if (old == null || old.getSbn().getInstanceId() == null) {
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 0dff7d12ce74..d996fe46a4f6 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -169,13 +169,15 @@ final class InstallParams extends HandlerParams {
final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
mOriginInfo.mResolvedPath, mPackageAbiOverride);
if (sizeBytes >= 0) {
- try {
- mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
- mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
- mPackageAbiOverride);
- } catch (Installer.InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
+ synchronized (mPm.mInstallLock) {
+ try {
+ mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
+ mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
+ mPackageAbiOverride);
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 55355d81ffe2..c8bd2c0bcecf 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -632,11 +632,15 @@ public class Installer extends SystemService {
}
}
- public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
- throws InstallerException {
+ /**
+ * Deletes cache from specified uuid until targetFreeBytes amount of space is free.
+ * flag denotes aggressive or non-aggresive mode where cache under quota is eligible or not
+ * respectively for clearing.
+ */
+ public void freeCache(String uuid, long targetFreeBytes, int flags) throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.freeCache(uuid, targetFreeBytes, cacheReservedBytes, flags);
+ mInstalld.freeCache(uuid, targetFreeBytes, flags);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e845aec9c6e0..a5b42f03b6df 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -46,6 +46,7 @@ import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.SharedLibraryInfo;
@@ -65,6 +66,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.F2fsUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.apphibernation.AppHibernationManagerInternal;
@@ -141,6 +143,7 @@ public class PackageDexOptimizer {
private final Injector mInjector;
+ private final Context mContext;
private static final Random sRandom = new Random();
PackageDexOptimizer(Installer installer, Object installLock, Context context,
@@ -159,6 +162,7 @@ public class PackageDexOptimizer {
}
protected PackageDexOptimizer(PackageDexOptimizer from) {
+ this.mContext = from.mContext;
this.mInstaller = from.mInstaller;
this.mInstallLock = from.mInstallLock;
this.mDexoptWakeLock = from.mDexoptWakeLock;
@@ -169,6 +173,7 @@ public class PackageDexOptimizer {
@VisibleForTesting
PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock,
Context context, String wakeLockTag) {
+ this.mContext = context;
this.mInstaller = installer;
this.mInstallLock = installLock;
@@ -434,6 +439,13 @@ public class PackageDexOptimizer {
long endTime = System.currentTimeMillis();
packageStats.setCompileTime(path, (int)(endTime - startTime));
}
+ if (oatDir != null) {
+ // Release odex/vdex compressed blocks to save user space.
+ // Compression support will be checked in F2fsUtils.
+ // The system app may be dexed, oatDir may be null, skip this situation.
+ final ContentResolver resolver = mContext.getContentResolver();
+ F2fsUtils.releaseCompressedBlocks(resolver, new File(oatDir));
+ }
return DEX_OPT_PERFORMED;
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fc266c86ccef..de1c2ad44d67 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -107,9 +107,13 @@ import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Random;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.function.IntPredicate;
import java.util.function.Supplier;
@@ -1436,13 +1440,74 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ static class ParentChildSessionMap {
+ private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
+
+ private final Comparator<PackageInstallerSession> mSessionCreationComparator =
+ Comparator.comparingLong((PackageInstallerSession sess) -> sess.createdMillis)
+ .thenComparingInt(sess -> sess.sessionId);
+
+ ParentChildSessionMap() {
+ mSessionMap = new TreeMap<>(mSessionCreationComparator);
+ }
+
+ boolean containsSession() {
+ return !(mSessionMap.isEmpty());
+ }
+
+ private void addParentSession(PackageInstallerSession session) {
+ if (!mSessionMap.containsKey(session)) {
+ mSessionMap.put(session, new TreeSet<>(mSessionCreationComparator));
+ }
+ }
+
+ private void addChildSession(PackageInstallerSession session,
+ PackageInstallerSession parentSession) {
+ addParentSession(parentSession);
+ mSessionMap.get(parentSession).add(session);
+ }
+
+ void addSession(PackageInstallerSession session,
+ PackageInstallerSession parentSession) {
+ if (session.hasParentSessionId()) {
+ addChildSession(session, parentSession);
+ } else {
+ addParentSession(session);
+ }
+ }
+
+ void dump(String tag, IndentingPrintWriter pw) {
+ pw.println(tag + " install sessions:");
+ pw.increaseIndent();
+
+ for (Map.Entry<PackageInstallerSession, TreeSet<PackageInstallerSession>> entry
+ : mSessionMap.entrySet()) {
+ PackageInstallerSession parentSession = entry.getKey();
+ pw.print(tag + " ");
+ parentSession.dump(pw);
+ pw.println();
+ pw.increaseIndent();
+
+ for (PackageInstallerSession childSession : entry.getValue()) {
+ pw.print(tag + " Child ");
+ childSession.dump(pw);
+ pw.println();
+ }
+
+ pw.decreaseIndent();
+ }
+
+ pw.println();
+ pw.decreaseIndent();
+ }
+ }
+
void dump(IndentingPrintWriter pw) {
synchronized (mSessions) {
- pw.println("Active install sessions:");
- pw.increaseIndent();
+ ParentChildSessionMap activeSessionMap = new ParentChildSessionMap();
+ ParentChildSessionMap orphanedChildSessionMap = new ParentChildSessionMap();
+ ParentChildSessionMap finalizedSessionMap = new ParentChildSessionMap();
- List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
- List<PackageInstallerSession> orphanedChildSessions = new ArrayList<>();
int N = mSessions.size();
for (int i = 0; i < N; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
@@ -1452,47 +1517,28 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
: session;
// Do not print orphaned child sessions as active install sessions
if (rootSession == null) {
- orphanedChildSessions.add(session);
+ orphanedChildSessionMap.addSession(session, rootSession);
continue;
}
// Do not print finalized staged session as active install sessions
if (rootSession.isStagedAndInTerminalState()) {
- finalizedSessions.add(session);
+ finalizedSessionMap.addSession(session, rootSession);
continue;
}
- session.dump(pw);
- pw.println();
+ activeSessionMap.addSession(session, rootSession);
}
- pw.println();
- pw.decreaseIndent();
- if (!orphanedChildSessions.isEmpty()) {
+ activeSessionMap.dump("Active", pw);
+
+ if (orphanedChildSessionMap.containsSession()) {
// Presence of orphaned sessions indicate leak in cleanup for multi-package and
// should be cleaned up.
- pw.println("Orphaned install sessions:");
- pw.increaseIndent();
- N = orphanedChildSessions.size();
- for (int i = 0; i < N; i++) {
- final PackageInstallerSession session = orphanedChildSessions.get(i);
- session.dump(pw);
- pw.println();
- }
- pw.println();
- pw.decreaseIndent();
+ orphanedChildSessionMap.dump("Orphaned", pw);
}
- pw.println("Finalized install sessions:");
- pw.increaseIndent();
- N = finalizedSessions.size();
- for (int i = 0; i < N; i++) {
- final PackageInstallerSession session = finalizedSessions.get(i);
- session.dump(pw);
- pw.println();
- }
- pw.println();
- pw.decreaseIndent();
+ finalizedSessionMap.dump("Finalized", pw);
pw.println("Historical install sessions:");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e71ac1aab711..eba2f55feaf7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2909,7 +2909,6 @@ public class PackageManagerService extends IPackageManager.Stub
volumeUuid);
final boolean aggressive = (storageFlags
& StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
- final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags);
// 1. Pre-flight to determine if we have any chance to succeed
// 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
@@ -2926,10 +2925,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
// 4. Consider cached app data (above quotas)
- try {
- mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
- Installer.FLAG_FREE_CACHE_V2);
- } catch (InstallerException ignored) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2);
+ } catch (InstallerException ignored) {
+ }
}
if (file.getUsableSpace() >= bytes) return;
@@ -2953,10 +2953,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
// 8. Consider cached app data (below quotas)
- try {
- mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
- Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
- } catch (InstallerException ignored) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(volumeUuid, bytes,
+ Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+ } catch (InstallerException ignored) {
+ }
}
if (file.getUsableSpace() >= bytes) return;
@@ -2982,9 +2984,11 @@ public class PackageManagerService extends IPackageManager.Stub
// 12. Clear temp install session files
mInstallerService.freeStageDirs(volumeUuid);
} else {
- try {
- mInstaller.freeCache(volumeUuid, bytes, 0, 0);
- } catch (InstallerException ignored) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(volumeUuid, bytes, 0);
+ } catch (InstallerException ignored) {
+ }
}
}
if (file.getUsableSpace() >= bytes) return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3554cc02b9fc..fc59541af1ea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -81,6 +81,7 @@ import android.os.ServiceSpecificException;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.V4Signature;
@@ -2629,6 +2630,7 @@ class PackageManagerShellCommand extends ShellCommand {
if (userType == null) {
userType = UserInfo.getDefaultUserType(flags);
}
+ Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "shell_runCreateUser");
try {
if (UserManager.isUserTypeRestricted(userType)) {
// In non-split user mode, userId can only be SYSTEM
@@ -2645,6 +2647,8 @@ class PackageManagerShellCommand extends ShellCommand {
}
} catch (ServiceSpecificException e) {
getErrPrintWriter().println("Error: " + e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
}
if (info != null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index b4bd086af272..42c88b3eee8a 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -22,8 +22,6 @@ import android.app.Person;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSession;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.GetByDocumentIdRequest;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.RemoveByDocumentIdRequest;
@@ -56,11 +54,12 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.ShortcutService.DumpFilter;
@@ -88,7 +87,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -160,6 +159,8 @@ class ShortcutPackage extends ShortcutPackageItem {
private final Object mLock = new Object();
+ private final Executor mExecutor;
+
/**
* An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
*/
@@ -189,11 +190,8 @@ class ShortcutPackage extends ShortcutPackageItem {
private long mLastKnownForegroundElapsedTime;
- private boolean mIsInitilized;
-
- private boolean mRescanRequired;
- private boolean mIsNewApp;
- private List<ShortcutInfo> mManifestShortcuts;
+ @GuardedBy("mLock")
+ private boolean mIsAppSearchSchemaUpToDate;
private ShortcutPackage(ShortcutUser shortcutUser,
int packageUserId, String packageName, ShortcutPackageInfo spi) {
@@ -201,6 +199,7 @@ class ShortcutPackage extends ShortcutPackageItem {
spi != null ? spi : ShortcutPackageInfo.newEmpty());
mPackageUid = shortcutUser.mService.injectGetPackageUid(packageName, packageUserId);
+ mExecutor = BackgroundThread.getExecutor();
}
public ShortcutPackage(ShortcutUser shortcutUser, int packageUserId, String packageName) {
@@ -245,11 +244,11 @@ class ShortcutPackage extends ShortcutPackageItem {
final String query = String.format("%s:-%s AND %s:%s",
AppSearchShortcutInfo.KEY_FLAGS, ShortcutInfo.FLAG_SHADOW,
AppSearchShortcutInfo.KEY_DISABLED_REASON, restoreBlockReason);
- forEachShortcutMutateIf(query, si -> {
+ forEachShortcutMutate(si -> {
if (restoreBlockReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED
&& !si.hasFlags(ShortcutInfo.FLAG_SHADOW)
&& si.getDisabledReason() == restoreBlockReason) {
- return false;
+ return;
}
si.clearFlags(ShortcutInfo.FLAG_SHADOW);
@@ -257,7 +256,6 @@ class ShortcutPackage extends ShortcutPackageItem {
if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
si.addFlags(ShortcutInfo.FLAG_DISABLED);
}
- return true;
});
// Because some launchers may not have been restored (e.g. allowBackup=false),
// we need to re-calculate the pinned shortcuts.
@@ -270,8 +268,7 @@ class ShortcutPackage extends ShortcutPackageItem {
@Nullable
public ShortcutInfo findShortcutById(@Nullable final String id) {
if (id == null) return null;
- final List<ShortcutInfo> ret = getShortcutById(Collections.singleton(id));
- return (ret == null || ret.isEmpty()) ? null : ret.get(0);
+ return mShortcuts.get(id);
}
public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
@@ -337,9 +334,8 @@ class ShortcutPackage extends ShortcutPackageItem {
* Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
*/
private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
- final ShortcutInfo shortcut = findShortcutById(id);
+ final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
- removeShortcut(id);
mShortcutUser.mService.removeIconLocked(shortcut);
shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
| ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
@@ -435,7 +431,8 @@ class ShortcutPackage extends ShortcutPackageItem {
}
changedShortcuts.add(shortcut);
- deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null;
+ deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true,
+ /*ignorePersistedShortcuts=*/ true) != null;
}
} else {
// It's an update case.
@@ -449,16 +446,14 @@ class ShortcutPackage extends ShortcutPackageItem {
forceReplaceShortcutInner(newShortcut);
if (isAppSearchEnabled()) {
- mShortcutUser.mService.injectPostToHandler(() -> awaitInAppSearch("reportUsage",
- session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.reportUsage(
- new ReportUsageRequest.Builder(
- getPackageName(), newShortcut.getId()).build(),
- mShortcutUser.mExecutor,
- result -> future.complete(result.isSuccess()));
- return future;
- }));
+ runAsSystem(() -> fromAppSearch().thenAccept(session ->
+ session.reportUsage(new ReportUsageRequest.Builder(
+ getPackageName(), newShortcut.getId()).build(), mExecutor, result -> {
+ if (!result.isSuccess()) {
+ Slog.e(TAG, "Failed to report usage via AppSearch. "
+ + result.getErrorMessage());
+ }
+ })));
}
return deleted;
}
@@ -470,12 +465,7 @@ class ShortcutPackage extends ShortcutPackageItem {
*/
private List<ShortcutInfo> removeOrphans() {
final List<ShortcutInfo> removeList = new ArrayList<>(1);
- final String query = String.format("%s OR %s OR %s OR %s",
- AppSearchShortcutInfo.QUERY_IS_PINNED,
- AppSearchShortcutInfo.QUERY_IS_DYNAMIC,
- AppSearchShortcutInfo.QUERY_IS_MANIFEST,
- AppSearchShortcutInfo.QUERY_IS_CACHED);
- forEachShortcut(query, si -> {
+ forEachShortcut(si -> {
if (si.isAlive()) return;
removeList.add(si);
});
@@ -484,7 +474,6 @@ class ShortcutPackage extends ShortcutPackageItem {
forceDeleteShortcutInner(removeList.get(i).getId());
}
}
-
return removeList;
}
@@ -493,29 +482,21 @@ class ShortcutPackage extends ShortcutPackageItem {
*
* @return List of shortcuts that actually got removed.
*/
- public List<ShortcutInfo> deleteAllDynamicShortcuts(boolean ignoreInvisible) {
+ public List<ShortcutInfo> deleteAllDynamicShortcuts() {
final long now = mShortcutUser.mService.injectCurrentTimeMillis();
- final String query;
- if (!ignoreInvisible) {
- query = AppSearchShortcutInfo.QUERY_IS_DYNAMIC;
- } else {
- query = String.format("%s %s",
- AppSearchShortcutInfo.QUERY_IS_DYNAMIC,
- AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER);
- }
- final boolean[] changed = new boolean[1];
- forEachShortcutMutateIf(query, si -> {
- if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
- changed[0] = true;
+ boolean changed = false;
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isDynamic() && si.isVisibleToPublisher()) {
+ changed = true;
si.setTimestamp(now);
si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
si.setRank(0); // It may still be pinned, so clear the rank.
- return true;
}
- return false;
- });
- if (changed[0]) {
+ }
+ removeAllShortcutsAsync();
+ if (changed) {
return removeOrphans();
}
return null;
@@ -528,10 +509,11 @@ class ShortcutPackage extends ShortcutPackageItem {
* @return The deleted shortcut, or null if it was not actually removed because it is either
* pinned or cached.
*/
- public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
+ public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
+ boolean ignorePersistedShortcuts) {
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ ShortcutInfo.DISABLED_REASON_NOT_DISABLED, ignorePersistedShortcuts);
}
/**
@@ -542,9 +524,9 @@ class ShortcutPackage extends ShortcutPackageItem {
* it's still pinned.
*/
private ShortcutInfo disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
- int disabledReason) {
+ int disabledReason, boolean ignorePersistedShortcuts) {
return deleteOrDisableWithId(shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false,
- ignoreInvisible, disabledReason);
+ ignoreInvisible, disabledReason, ignorePersistedShortcuts);
}
/**
@@ -560,7 +542,7 @@ class ShortcutPackage extends ShortcutPackageItem {
}
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ ShortcutInfo.DISABLED_REASON_NOT_DISABLED, /*ignorePersistedShortcuts=*/ false);
}
/**
@@ -574,7 +556,8 @@ class ShortcutPackage extends ShortcutPackageItem {
int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
int disabledReason) {
final ShortcutInfo deleted = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
- overrideImmutable, ignoreInvisible, disabledReason);
+ overrideImmutable, ignoreInvisible, disabledReason,
+ /*ignorePersistedShortcuts=*/ false);
// If disabled id still exists, it is pinned and we need to update the disabled message.
mutateShortcut(shortcutId, null, disabled -> {
@@ -593,7 +576,8 @@ class ShortcutPackage extends ShortcutPackageItem {
@Nullable
private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
- boolean overrideImmutable, boolean ignoreInvisible, int disabledReason) {
+ boolean overrideImmutable, boolean ignoreInvisible, int disabledReason,
+ boolean ignorePersistedShortcuts) {
Preconditions.checkState(
(disable == (disabledReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED)),
"disable and disabledReason disagree: " + disable + " vs " + disabledReason);
@@ -606,8 +590,10 @@ class ShortcutPackage extends ShortcutPackageItem {
if (!overrideImmutable) {
ensureNotImmutable(oldShortcut, /*ignoreInvisible=*/ true);
}
+ if (!ignorePersistedShortcuts) {
+ removeShortcutAsync(shortcutId);
+ }
if (oldShortcut.isPinned() || oldShortcut.isCached()) {
-
mutateShortcut(oldShortcut.getId(), oldShortcut, si -> {
si.setRank(0);
si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
@@ -672,21 +658,19 @@ class ShortcutPackage extends ShortcutPackageItem {
pinnedShortcuts.addAll(pinned);
});
// Then, update the pinned state if necessary.
- final List<ShortcutInfo> pinned = getShortcutById(pinnedShortcuts);
+ final List<ShortcutInfo> pinned = findAll(pinnedShortcuts);
if (pinned != null) {
pinned.forEach(si -> {
if (!si.isPinned()) {
si.addFlags(ShortcutInfo.FLAG_PINNED);
}
});
- saveShortcut(pinned);
}
- forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_PINNED, si -> {
+ forEachShortcutMutate(si -> {
if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
si.clearFlags(ShortcutInfo.FLAG_PINNED);
- return true;
+ return;
}
- return false;
});
// Lastly, remove the ones that are no longer pinned, cached nor dynamic.
@@ -777,9 +761,9 @@ class ShortcutPackage extends ShortcutPackageItem {
/**
* Find all shortcuts that match {@code query}.
*/
- public void findAll(@NonNull List<ShortcutInfo> result, @Nullable String query,
+ public void findAll(@NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> filter, int cloneFlag) {
- findAll(result, query, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
+ findAll(result, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
}
/**
@@ -790,7 +774,7 @@ class ShortcutPackage extends ShortcutPackageItem {
* adjusted for the caller too.
*/
public void findAll(@NonNull List<ShortcutInfo> result,
- @Nullable String query, @Nullable Predicate<ShortcutInfo> filter, int cloneFlag,
+ @Nullable Predicate<ShortcutInfo> filter, int cloneFlag,
@Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
if (getPackageInfo().isShadow()) {
// Restored and the app not installed yet, so don't return any.
@@ -802,9 +786,8 @@ class ShortcutPackage extends ShortcutPackageItem {
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
.getPinnedShortcutIds(getPackageName(), getPackageUserId());
- forEachShortcut(query == null ? "" : query, si ->
- filter(result, filter, cloneFlag, callingLauncher, pinnedByCallerSet,
- getPinnedByAnyLauncher, si));
+ forEachShortcut(si -> filter(result, filter, cloneFlag, callingLauncher, pinnedByCallerSet,
+ getPinnedByAnyLauncher, si));
}
/**
@@ -837,35 +820,12 @@ class ShortcutPackage extends ShortcutPackageItem {
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
.getPinnedShortcutIds(getPackageName(), getPackageUserId());
- final List<ShortcutInfo> shortcuts = getShortcutById(ids);
- if (shortcuts != null) {
- for (ShortcutInfo si : shortcuts) {
- filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
- getPinnedByAnyLauncher, si);
- }
+ for (ShortcutInfo si : mShortcuts.values()) {
+ filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
+ getPinnedByAnyLauncher, si);
}
}
- /**
- * Find all pinned shortcuts that match {@code query}.
- */
- public void findAllPinned(@NonNull List<ShortcutInfo> result,
- @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
- @Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
- if (getPackageInfo().isShadow()) {
- // Restored and the app not installed yet, so don't return any.
- return;
- }
- final ShortcutService s = mShortcutUser.mService;
-
- // Set of pinned shortcuts by the calling launcher.
- final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
- : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
- .getPinnedShortcutIds(getPackageName(), getPackageUserId());
- mShortcuts.values().forEach(si -> filter(result, query, cloneFlag, callingLauncher,
- pinnedByCallerSet, getPinnedByAnyLauncher, si));
- }
-
private void filter(@NonNull final List<ShortcutInfo> result,
@Nullable final Predicate<ShortcutInfo> query, final int cloneFlag,
@Nullable final String callingLauncher,
@@ -930,8 +890,8 @@ class ShortcutPackage extends ShortcutPackageItem {
// Get the list of all dynamic shortcuts in this package.
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, AppSearchShortcutInfo.QUERY_IS_NON_MANIFEST_VISIBLE,
- ShortcutInfo::isNonManifestVisible, ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
for (int i = 0; i < shortcuts.size(); i++) {
@@ -975,8 +935,8 @@ class ShortcutPackage extends ShortcutPackageItem {
// Get the list of all dynamic shortcuts in this package
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, AppSearchShortcutInfo.QUERY_IS_NON_MANIFEST_VISIBLE,
- ShortcutInfo::isNonManifestVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
int sharingShortcutCount = 0;
for (int i = 0; i < shortcuts.size(); i++) {
@@ -1129,53 +1089,37 @@ class ShortcutPackage extends ShortcutPackageItem {
getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
}
getPackageInfo().updateFromPackageInfo(pi);
- if (isAppSearchEnabled()) {
- // Save the states in memory and resume package rescan when needed
- mRescanRequired = true;
- mIsNewApp = isNewApp;
- mManifestShortcuts = newManifestShortcutList;
- } else {
- rescanPackage(isNewApp, newManifestShortcutList);
- }
- return true; // true means changed.
- }
-
- private void rescanPackage(
- final boolean isNewApp, @NonNull final List<ShortcutInfo> newManifestShortcutList) {
- final ShortcutService s = mShortcutUser.mService;
final long newVersionCode = getPackageInfo().getVersionCode();
// See if there are any shortcuts that were prevented restoring because the app was of a
// lower version, and re-enable them.
{
- forEachShortcutMutateIf(
- AppSearchShortcutInfo.QUERY_DISABLED_REASON_VERSION_LOWER, si -> {
- if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
- return false;
- }
- if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
- if (ShortcutService.DEBUG) {
- Slog.d(TAG,
- String.format(
- "Shortcut %s require version %s, still not restored.",
- si.getId(),
- getPackageInfo().getBackupSourceVersionCode()));
- }
- return false;
+ forEachShortcutMutate(si -> {
+ if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
+ return;
+ }
+ if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG,
+ String.format(
+ "Shortcut %s require version %s, still not restored.",
+ si.getId(),
+ getPackageInfo().getBackupSourceVersionCode()));
}
- Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
- si.clearFlags(ShortcutInfo.FLAG_DISABLED);
- si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- return true;
- });
+ return;
+ }
+ Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
+ si.clearFlags(ShortcutInfo.FLAG_DISABLED);
+ si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ });
}
// For existing shortcuts, update timestamps if they have any resources.
// Also check if shortcuts' activities are still main activities. Otherwise, disable them.
if (!isNewApp) {
final Resources publisherRes = getPackageResources();
- forEachShortcutMutateIf(si -> {
+ forEachShortcutMutate(si -> {
// Disable dynamic shortcuts whose target activity is gone.
if (si.isDynamic()) {
if (si.getActivity() == null) {
@@ -1187,15 +1131,16 @@ class ShortcutPackage extends ShortcutPackageItem {
"%s is no longer main activity. Disabling shorcut %s.",
getPackageName(), si.getId()));
if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
- ShortcutInfo.DISABLED_REASON_APP_CHANGED) != null) {
- return false; // Actually removed.
+ ShortcutInfo.DISABLED_REASON_APP_CHANGED,
+ /*ignorePersistedShortcuts*/ false) != null) {
+ return;
}
// Still pinned, so fall-through and possibly update the resources.
}
}
if (!si.hasAnyResources() || publisherRes == null) {
- return false;
+ return;
}
if (!si.isOriginallyFromManifest()) {
@@ -1206,7 +1151,6 @@ class ShortcutPackage extends ShortcutPackageItem {
// from resource names. (We don't allow resource strings for
// non-manifest at the moment, but icons can still be resources.)
si.setTimestamp(s.injectCurrentTimeMillis());
- return true;
});
}
@@ -1222,7 +1166,8 @@ class ShortcutPackage extends ShortcutPackageItem {
// This will send a notification to the launcher, and also save .
// TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged()
s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null);
- mManifestShortcuts = null;
+
+ return true;
}
private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
@@ -1297,7 +1242,8 @@ class ShortcutPackage extends ShortcutPackageItem {
final String id = toDisableList.valueAt(i);
- disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
+ disableWithId(id, /* disable message =*/ null,
+ /* disable message resid */ 0,
/* overrideImmutable=*/ true, /*ignoreInvisible=*/ false,
ShortcutInfo.DISABLED_REASON_APP_CHANGED);
}
@@ -1338,7 +1284,8 @@ class ShortcutPackage extends ShortcutPackageItem {
service.wtf("Found manifest shortcuts in excess list.");
continue;
}
- deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
+ deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true,
+ /*ignorePersistedShortcuts=*/ true);
}
}
@@ -1494,12 +1441,11 @@ class ShortcutPackage extends ShortcutPackageItem {
final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
if (publisherRes != null) {
- forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_HAS_STRING_RESOURCE, si -> {
- if (!si.hasStringResources()) return false;
+ forEachShortcutMutate(si -> {
+ if (!si.hasStringResources()) return;
si.resolveResourceStrings(publisherRes);
si.setTimestamp(s.injectCurrentTimeMillis());
changedShortcuts.add(si);
- return true;
});
}
if (!CollectionUtils.isEmpty(changedShortcuts)) {
@@ -1549,13 +1495,11 @@ class ShortcutPackage extends ShortcutPackageItem {
final long now = s.injectCurrentTimeMillis();
// First, clear ranks for floating shortcuts.
- forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_FLOATING_AND_HAS_RANK, si -> {
+ forEachShortcutMutate(si -> {
if (si.isFloating() && si.getRank() != 0) {
si.setTimestamp(now);
si.setRank(0);
- return true;
}
- return false;
});
// Then adjust ranks. Ranks are unique for each activity, so we first need to sort
@@ -1581,7 +1525,7 @@ class ShortcutPackage extends ShortcutPackageItem {
}
// At this point, it must be dynamic.
if (!si.isDynamic()) {
- s.wtf("Non-dynamic shortcut found.");
+ s.wtf("Non-dynamic shortcut found. " + si.toInsecureString());
continue;
}
final int thisRank = rank++;
@@ -1745,13 +1689,10 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
if (!forBackup) {
- /**
- * Schema version should not be included in the backup because:
- * 1. Schemas in AppSearch are created from scratch on new device
- * 2. Shortcuts are restored from xml file (as opposed to from AppSearch) on new device
- */
- ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsInitilized)
- ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
+ synchronized (mLock) {
+ ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate)
+ ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
+ }
}
getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
@@ -1763,6 +1704,8 @@ class ShortcutPackage extends ShortcutPackageItem {
for (int j = 0; j < shareTargetSize; j++) {
mShareTargets.get(j).saveToXml(out);
}
+ saveShortcutsAsync(mShortcuts.values().stream().filter(ShortcutInfo::usesQuota)
+ .collect(Collectors.toList()));
}
out.endTag(null, TAG_ROOT);
@@ -1943,8 +1886,10 @@ class ShortcutPackage extends ShortcutPackageItem {
final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
- ret.mIsInitilized = ShortcutService.parseIntAttribute(parser, ATTR_SCHEMA_VERSON, 0)
- == AppSearchShortcutInfo.SCHEMA_VERSION;
+ synchronized (ret.mLock) {
+ ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
+ parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
+ }
ret.mApiCallCount =
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
@@ -2297,8 +2242,15 @@ class ShortcutPackage extends ShortcutPackageItem {
} else {
mPackageIdentifiers.remove(packageName);
}
- awaitInAppSearch(true, "Update visibility",
- session -> AndroidFuture.completedFuture(true));
+ synchronized (mLock) {
+ mIsAppSearchSchemaUpToDate = false;
+ }
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ fromAppSearch();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
@@ -2325,148 +2277,15 @@ class ShortcutPackage extends ShortcutPackageItem {
private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
Objects.requireNonNull(shortcuts);
- if (!isAppSearchEnabled()) {
- // If AppSearch isn't enabled, save it in memory and we are done.
- for (ShortcutInfo si : shortcuts) {
- mShortcuts.put(si.getId(), si);
- }
- return;
- }
- // Otherwise, save pinned shortcuts in memory.
- shortcuts.forEach(si -> {
- if (si.isPinned()) {
- mShortcuts.put(si.getId(), si);
- } else {
- mShortcuts.remove(si.getId());
- }
- });
- // Then proceed to app search.
- saveToAppSearch(shortcuts);
- }
-
- private void saveToAppSearch(@NonNull final Collection<ShortcutInfo> shortcuts) {
- Objects.requireNonNull(shortcuts);
- if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
- // No need to invoke AppSearch when there's nothing to save.
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Saving shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName() + " ids=["
- + shortcuts.stream().map(ShortcutInfo::getId)
- .collect(Collectors.joining(",")) + "]");
- }
- awaitInAppSearch("Saving shortcuts", session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.put(new PutDocumentsRequest.Builder()
- .addGenericDocuments(
- AppSearchShortcutInfo.toGenericDocuments(shortcuts))
- .build(),
- mShortcutUser.mExecutor,
- result -> {
- if (!result.isSuccess()) {
- for (AppSearchResult<Void> k : result.getFailures().values()) {
- Slog.e(TAG, k.getErrorMessage());
- }
- future.completeExceptionally(new RuntimeException(
- "Failed to save shortcuts"));
- return;
- }
- future.complete(true);
- });
- return future;
- });
- }
-
- /**
- * Removes shortcuts from AppSearch.
- */
- void removeShortcuts() {
- if (!isAppSearchEnabled()) {
- return;
+ for (ShortcutInfo si : shortcuts) {
+ mShortcuts.put(si.getId(), si);
}
- awaitInAppSearch("Removing all shortcuts from " + getPackageName(), session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- future.completeExceptionally(
- new RuntimeException(result.getErrorMessage()));
- return;
- }
- future.complete(true);
- });
- return future;
- });
- }
-
- private void removeShortcut(@NonNull final String id) {
- Objects.requireNonNull(id);
- mShortcuts.remove(id);
- if (!isAppSearchEnabled()) {
- return;
- }
- awaitInAppSearch("Removing shortcut with id=" + id, session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.remove(
- new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(id).build(),
- mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- final Map<String, AppSearchResult<Void>> failures =
- result.getFailures();
- for (String key : failures.keySet()) {
- Slog.e(TAG, "Failed deleting " + key + ", error message:"
- + failures.get(key).getErrorMessage());
- }
- future.completeExceptionally(new RuntimeException(
- "Failed to delete shortcut: " + id));
- return;
- }
- future.complete(true);
- });
- return future;
- });
}
@Nullable
- private List<ShortcutInfo> getShortcutById(@NonNull final Collection<String> ids) {
- final List<String> shortcutIds = new ArrayList<>(1);
- for (String id : ids) {
- if (id != null) {
- shortcutIds.add(id);
- }
- }
- if (!isAppSearchEnabled()) {
- final List<ShortcutInfo> ret = new ArrayList<>(1);
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- ShortcutInfo si = mShortcuts.valueAt(i);
- if (shortcutIds.contains(si.getId())) {
- ret.add(si);
- }
- }
- return ret;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Getting shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName() + " ids: [" + String.join(",", ids) + "]");
- }
- return awaitInAppSearch("Getting shortcut by id", session -> {
- final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
- session.getByDocumentId(
- new GetByDocumentIdRequest.Builder(getPackageName())
- .addIds(shortcutIds).build(),
- mShortcutUser.mExecutor,
- results -> {
- final List<ShortcutInfo> ret = new ArrayList<>(1);
- Map<String, GenericDocument> documents = results.getSuccesses();
- for (GenericDocument doc : documents.values()) {
- final ShortcutInfo info = new AppSearchShortcutInfo(doc)
- .toShortcutInfo(mShortcutUser.getUserId());
- ret.add(info);
- }
- future.complete(ret);
- });
- return future;
- });
+ List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
+ return ids.stream().map(mShortcuts::get)
+ .filter(Objects::nonNull).collect(Collectors.toList());
}
private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) {
@@ -2482,40 +2301,9 @@ class ShortcutPackage extends ShortcutPackageItem {
}
private void forEachShortcutMutate(@NonNull final Consumer<ShortcutInfo> cb) {
- forEachShortcutMutateIf(si -> {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ ShortcutInfo si = mShortcuts.valueAt(i);
cb.accept(si);
- return true;
- });
- }
-
- private void forEachShortcutMutateIf(@NonNull final Function<ShortcutInfo, Boolean> cb) {
- forEachShortcutMutateIf("", cb);
- }
-
- private void forEachShortcutMutateIf(@NonNull final String query,
- @NonNull final Function<ShortcutInfo, Boolean> cb) {
- if (!isAppSearchEnabled()) {
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- ShortcutInfo si = mShortcuts.valueAt(i);
- cb.apply(si);
- }
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Changing shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- final SearchResults res = awaitInAppSearch("Mutating shortcuts", session ->
- AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
- if (res == null) return;
- List<ShortcutInfo> shortcuts = getNextPage(res);
- while (!shortcuts.isEmpty()) {
- final List<ShortcutInfo> changed = new ArrayList<>(1);
- for (ShortcutInfo si : shortcuts) {
- if (cb.apply(si)) changed.add(si);
- }
- saveShortcut(changed);
- shortcuts = getNextPage(res);
}
}
@@ -2526,114 +2314,10 @@ class ShortcutPackage extends ShortcutPackageItem {
private void forEachShortcutStopWhen(
@NonNull final String query, @NonNull final Function<ShortcutInfo, Boolean> cb) {
- if (!isAppSearchEnabled()) {
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = mShortcuts.valueAt(i);
- if (cb.apply(si)) {
- return;
- }
- }
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Iterating shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- final SearchResults res = awaitInAppSearch("Iterating shortcuts", session ->
- AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
- if (res == null) return;
- List<ShortcutInfo> shortcuts = getNextPage(res);
- while (!shortcuts.isEmpty()) {
- for (ShortcutInfo si : shortcuts) {
- if (cb.apply(si)) return;
- }
- shortcuts = getNextPage(res);
- }
- }
-
- private List<ShortcutInfo> getNextPage(@NonNull final SearchResults res) {
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Get next page for search result for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
- final List<ShortcutInfo> ret = new ArrayList<>();
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- res.getNextPage(mShortcutUser.mExecutor, nextPage -> {
- if (!nextPage.isSuccess()) {
- future.complete(ret);
- return;
- }
- final List<SearchResult> results = nextPage.getResultValue();
- if (results.isEmpty()) {
- future.complete(ret);
- return;
- }
- final List<ShortcutInfo> page = new ArrayList<>(results.size());
- for (SearchResult result : results) {
- final ShortcutInfo si = new AppSearchShortcutInfo(result.getGenericDocument())
- .toShortcutInfo(mShortcutUser.getUserId());
- page.add(si);
- }
- ret.addAll(page);
- future.complete(ret);
- });
- return ConcurrentUtils.waitForFutureNoInterrupt(future,
- "Getting next batch of shortcuts");
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
- @Nullable
- private <T> T awaitInAppSearch(
- @NonNull final String description,
- @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
- return awaitInAppSearch(false, description, cb);
- }
-
- @Nullable
- private <T> T awaitInAppSearch(
- final boolean forceReset,
- @NonNull final String description,
- @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
- if (!isAppSearchEnabled()) {
- throw new IllegalStateException(
- "awaitInAppSearch called when app search integration is disabled");
- }
- synchronized (mLock) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
- final long callingIdentity = Binder.clearCallingIdentity();
- final AppSearchManager.SearchContext searchContext =
- new AppSearchManager.SearchContext.Builder(getPackageName()).build();
- try (AppSearchSession session = ConcurrentUtils.waitForFutureNoInterrupt(
- mShortcutUser.getAppSearch(searchContext), "Resetting app search")) {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
- .build());
- final boolean wasInitialized = mIsInitilized;
- if (!wasInitialized || forceReset) {
- ConcurrentUtils.waitForFutureNoInterrupt(
- setupSchema(session), "Setting up schema");
- }
- mIsInitilized = true;
- if (!wasInitialized) {
- restoreParsedShortcuts(false);
- }
- if (mRescanRequired) {
- mRescanRequired = false;
- rescanPackage(mIsNewApp, mManifestShortcuts);
- }
- return ConcurrentUtils.waitForFutureNoInterrupt(cb.apply(session), description);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to initiate app search for shortcut package "
- + getPackageName() + " user " + mShortcutUser.getUserId(), e);
- return null;
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- StrictMode.setThreadPolicy(oldPolicy);
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (cb.apply(si)) {
+ return;
}
}
}
@@ -2657,7 +2341,7 @@ class ShortcutPackage extends ShortcutPackageItem {
}
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
session.setSchema(
- schemaBuilder.build(), mShortcutUser.mExecutor, mShortcutUser.mExecutor, result -> {
+ schemaBuilder.build(), mExecutor, mShortcutUser.mExecutor, result -> {
if (!result.isSuccess()) {
future.completeExceptionally(
new IllegalArgumentException(result.getErrorMessage()));
@@ -2677,29 +2361,6 @@ class ShortcutPackage extends ShortcutPackageItem {
.build();
}
- /**
- * Replace shortcuts parsed from xml file.
- */
- void restoreParsedShortcuts() {
- restoreParsedShortcuts(true);
- }
-
- private void restoreParsedShortcuts(final boolean replace) {
- if (ShortcutService.DEBUG_REBOOT) {
- if (replace) {
- Slog.d(TAG, "Replacing all shortcuts with the ones parsed from xml for user="
- + mShortcutUser.getUserId() + " pkg=" + getPackageName());
- } else {
- Slog.d(TAG, "Restoring pinned shortcuts from xml for user="
- + mShortcutUser.getUserId() + " pkg=" + getPackageName());
- }
- }
- if (replace) {
- removeShortcuts();
- }
- saveToAppSearch(mShortcuts.values());
- }
-
private boolean verifyRanksSequential(List<ShortcutInfo> list) {
boolean failed = false;
@@ -2713,4 +2374,133 @@ class ShortcutPackage extends ShortcutPackageItem {
}
return failed;
}
+
+ // Async Operations
+
+ /**
+ * Removes all shortcuts from AppSearch.
+ */
+ void removeAllShortcutsAsync() {
+ if (!isAppSearchEnabled()) {
+ return;
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session ->
+ session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
+ if (!result.isSuccess()) {
+ Slog.e(TAG, "Failed to remove shortcuts from AppSearch. "
+ + result.getErrorMessage());
+ }
+ })));
+ }
+
+ private void removeShortcutAsync(@NonNull final String... id) {
+ Objects.requireNonNull(id);
+ removeShortcutAsync(Arrays.asList(id));
+ }
+
+ private void removeShortcutAsync(@NonNull final Collection<String> ids) {
+ if (!isAppSearchEnabled()) {
+ return;
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session ->
+ session.remove(
+ new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(ids).build(),
+ mShortcutUser.mExecutor, result -> {
+ if (!result.isSuccess()) {
+ final Map<String, AppSearchResult<Void>> failures =
+ result.getFailures();
+ for (String key : failures.keySet()) {
+ Slog.e(TAG, "Failed deleting " + key + ", error message:"
+ + failures.get(key).getErrorMessage());
+ }
+ }
+ })));
+ }
+
+ private void saveShortcutsAsync(
+ @NonNull final Collection<ShortcutInfo> shortcuts) {
+ Objects.requireNonNull(shortcuts);
+ if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
+ // No need to invoke AppSearch when there's nothing to save.
+ return;
+ }
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Saving shortcuts async for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName() + " ids=["
+ + shortcuts.stream().map(ShortcutInfo::getId)
+ .collect(Collectors.joining(",")) + "]");
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+ if (shortcuts.isEmpty()) {
+ return;
+ }
+ session.put(new PutDocumentsRequest.Builder()
+ .addGenericDocuments(
+ AppSearchShortcutInfo.toGenericDocuments(shortcuts))
+ .build(),
+ mShortcutUser.mExecutor,
+ result -> {
+ if (!result.isSuccess()) {
+ for (AppSearchResult<Void> k : result.getFailures().values()) {
+ Slog.e(TAG, k.getErrorMessage());
+ }
+ }
+ });
+ }));
+ }
+
+ @VisibleForTesting
+ void getTopShortcutsFromPersistence(AndroidFuture<List<ShortcutInfo>> cb) {
+ runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+ SearchResults res = session.search("", getSearchSpec());
+ res.getNextPage(mShortcutUser.mExecutor, results -> {
+ if (!results.isSuccess()) {
+ cb.completeExceptionally(new IllegalStateException(results.getErrorMessage()));
+ return;
+ }
+ cb.complete(results.getResultValue().stream()
+ .map(SearchResult::getGenericDocument)
+ .map(AppSearchShortcutInfo::new)
+ .map(si -> si.toShortcutInfo(mShortcutUser.getUserId()))
+ .collect(Collectors.toList()));
+ });
+ }));
+ }
+
+ @NonNull
+ private AndroidFuture<AppSearchSession> fromAppSearch() {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ final AppSearchManager.SearchContext searchContext =
+ new AppSearchManager.SearchContext.Builder(getPackageName()).build();
+ AndroidFuture<AppSearchSession> future = null;
+ try {
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
+ .build());
+ future = mShortcutUser.getAppSearch(searchContext);
+ synchronized (mLock) {
+ if (!mIsAppSearchSchemaUpToDate) {
+ future = future.thenCompose(this::setupSchema);
+ }
+ mIsAppSearchSchemaUpToDate = true;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to invoke app search pkg="
+ + getPackageName() + " user=" + mShortcutUser.getUserId(), e);
+ Objects.requireNonNull(future).completeExceptionally(e);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ return Objects.requireNonNull(future);
+ }
+
+ private void runAsSystem(@NonNull final Runnable fn) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ fn.run();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index e21c9c2d0086..c1f57f970127 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -467,7 +467,7 @@ class ShortcutRequestPinProcessor {
launcher.attemptToRestoreIfNeededAndSave();
if (launcher.hasPinned(original)) {
if (DEBUG) {
- Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
+ Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
}
return true;
}
@@ -517,7 +517,8 @@ class ShortcutRequestPinProcessor {
if (DEBUG) {
Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
}
- ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
+ ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false,
+ /*wasPushedOut=*/ false);
}
ps.adjustRanks(); // Shouldn't be needed, but just in case.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 687a165e4032..85b743594b75 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1965,13 +1965,12 @@ public class ShortcutService extends IShortcutService.Stub {
ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
ps.findAll(cachedOrPinned,
- AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED,
(ShortcutInfo si) -> si.isVisibleToPublisher()
&& si.isDynamic() && (si.isCached() || si.isPinned()),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
// First, remove all un-pinned and non-cached; dynamic shortcuts
- removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ removedShortcuts = ps.deleteAllDynamicShortcuts();
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
@@ -2381,7 +2380,8 @@ public class ShortcutService extends IShortcutService.Stub {
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
continue;
}
- ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+ ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true,
+ /*wasPushedOut*/ false);
if (removed == null) {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
@@ -2412,11 +2412,10 @@ public class ShortcutService extends IShortcutService.Stub {
userId);
// Dynamic shortcuts that are either cached or pinned will not get deleted.
ps.findAll(changedShortcuts,
- AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED,
(ShortcutInfo si) -> si.isVisibleToPublisher()
&& si.isDynamic() && (si.isCached() || si.isPinned()),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ removedShortcuts = ps.deleteAllDynamicShortcuts();
changedShortcuts = prepareChangedShortcuts(
changedShortcuts, null, removedShortcuts, ps);
}
@@ -2475,10 +2474,8 @@ public class ShortcutService extends IShortcutService.Stub {
| (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
| (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
| (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
- final String query = AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER + " "
- + createQuery(matchDynamic, matchPinned, matchManifest, matchCached);
return getShortcutsWithQueryLocked(
- packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, query,
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
(ShortcutInfo si) ->
si.isVisibleToPublisher()
&& (si.getFlags() & shortcutFlags) != 0);
@@ -2542,13 +2539,12 @@ public class ShortcutService extends IShortcutService.Stub {
@GuardedBy("mLock")
private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
- @UserIdInt int userId, int cloneFlags, @NonNull final String query,
- @NonNull Predicate<ShortcutInfo> filter) {
+ @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> filter) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.findAll(ret, query, filter, cloneFlags);
+ ps.findAll(ret, filter, cloneFlags);
return new ParceledListSlice<>(setReturnedByServer(ret));
}
@@ -2955,27 +2951,14 @@ public class ShortcutService extends IShortcutService.Stub {
((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0);
queryFlags |= (getPinnedByAnyLauncher ? ShortcutQuery.FLAG_MATCH_PINNED : 0);
- final boolean matchPinnedOnly =
- ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0)
- && ((queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) == 0)
- && ((queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) == 0)
- && ((queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) == 0);
-
final Predicate<ShortcutInfo> filter = getFilterFromQuery(ids, locusIds, changedSince,
componentName, queryFlags, getPinnedByAnyLauncher);
- if (matchPinnedOnly) {
- p.findAllPinned(ret, filter, cloneFlag, callingPackage, launcherUserId,
- getPinnedByAnyLauncher);
- } else if (ids != null && !ids.isEmpty()) {
+ if (ids != null && !ids.isEmpty()) {
p.findAllByIds(ret, ids, filter, cloneFlag, callingPackage, launcherUserId,
getPinnedByAnyLauncher);
} else {
- final boolean matchDynamic = (queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
- final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
- final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
- final boolean matchCached = (queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
- p.findAll(ret, createQuery(matchDynamic, matchPinned, matchManifest, matchCached),
- filter, cloneFlag, callingPackage, launcherUserId, getPinnedByAnyLauncher);
+ p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
+ getPinnedByAnyLauncher);
}
}
@@ -3090,8 +3073,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (sp != null) {
// List the shortcuts that are pinned only, these will get removed.
removedShortcuts = new ArrayList<>();
- sp.findAll(removedShortcuts, AppSearchShortcutInfo.QUERY_IS_VISIBLE_PINNED_ONLY,
- (ShortcutInfo si) -> si.isVisibleToPublisher()
+ sp.findAll(removedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
&& si.isPinned() && !si.isCached() && !si.isDynamic()
&& !si.isDeclaredInManifest(),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO,
@@ -3183,8 +3165,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (doCache) {
if (si.isLongLived()) {
- sp.mutateShortcut(si.getId(), si,
- shortcut -> shortcut.addFlags(cacheFlags));
+ si.addFlags(cacheFlags);
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
@@ -3195,21 +3176,20 @@ public class ShortcutService extends IShortcutService.Stub {
}
} else {
ShortcutInfo removed = null;
- sp.mutateShortcut(si.getId(), si, shortcut ->
- shortcut.clearFlags(cacheFlags));
+ si.clearFlags(cacheFlags);
if (!si.isDynamic() && !si.isCached()) {
removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
}
- if (removed != null) {
- if (removedShortcuts == null) {
- removedShortcuts = new ArrayList<>(1);
- }
- removedShortcuts.add(removed);
- } else {
+ if (removed == null) {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
changedShortcuts.add(si);
+ } else {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(removed);
}
}
}
@@ -5084,8 +5064,7 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) {
final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
if (pkg == null) return;
-
- pkg.mutateShortcut(shortcutId, null, cb);
+ cb.accept(pkg.findShortcutById(shortcutId));
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index e66cb03950cc..408f045f47b8 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -186,7 +186,7 @@ class ShortcutUser {
final ShortcutPackage removed = mPackages.remove(packageName);
if (removed != null) {
- removed.removeShortcuts();
+ removed.removeAllShortcutsAsync();
}
mService.cleanupBitmapsForPackage(mUserId, packageName);
@@ -577,7 +577,7 @@ class ShortcutUser {
Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ " Existing non-manifeset shortcuts will be overwritten.");
}
- sp.restoreParsedShortcuts();
+ sp.removeAllShortcutsAsync();
addPackage(sp);
restoredPackages[0]++;
restoredShortcuts[0] += sp.getShortcutCount();
@@ -714,6 +714,7 @@ class ShortcutUser {
.setSubtype(totalSharingShortcutCount));
}
+ @NonNull
AndroidFuture<AppSearchSession> getAppSearch(
@NonNull final AppSearchManager.SearchContext searchContext) {
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 962816c6dd92..0fb8475bc3af 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -899,7 +899,7 @@ public class DomainVerificationService extends SystemService
oldPkgState.getUserStates();
int oldUserStatesSize = oldUserStates.size();
if (oldUserStatesSize > 0) {
- ArraySet<String> newWebDomains = mCollector.collectValidAutoVerifyDomains(newPkg);
+ ArraySet<String> newWebDomains = mCollector.collectAllWebDomains(newPkg);
for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize;
oldUserStatesIndex++) {
int userId = oldUserStates.keyAt(oldUserStatesIndex);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7826ddfc6401..0394d4cca110 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -95,7 +95,6 @@ import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.health.V2_0.IHealth;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
@@ -194,13 +193,13 @@ import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThrea
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.role.RoleManagerLocal;
-import com.android.server.BatteryService;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.health.HealthServiceWrapper;
import com.android.server.notification.NotificationManagerService;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
@@ -230,6 +229,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
@@ -358,7 +358,7 @@ public class StatsPullAtomService extends SystemService {
private File mBaseDir;
@GuardedBy("mHealthHalLock")
- private BatteryService.HealthServiceWrapper mHealthService;
+ private HealthServiceWrapper mHealthService;
@Nullable
@GuardedBy("mCpuTimePerThreadFreqLock")
@@ -807,10 +807,9 @@ public class StatsPullAtomService extends SystemService {
KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
// Initialize HealthService
- mHealthService = new BatteryService.HealthServiceWrapper();
try {
- mHealthService.init();
- } catch (RemoteException e) {
+ mHealthService = HealthServiceWrapper.create(null);
+ } catch (RemoteException | NoSuchElementException e) {
Slog.e(TAG, "failed to initialize healthHalWrapper");
}
@@ -3990,38 +3989,40 @@ public class StatsPullAtomService extends SystemService {
}
int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) {
- IHealth healthService = mHealthService.getLastService();
- if (healthService == null) {
+ if (mHealthService == null) {
return StatsManager.PULL_SKIP;
}
+ android.hardware.health.HealthInfo healthInfo;
try {
- healthService.getHealthInfo((result, value) -> {
- int pulledValue;
- switch(atomTag) {
- case FrameworkStatsLog.BATTERY_LEVEL:
- pulledValue = value.legacy.batteryLevel;
- break;
- case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
- pulledValue = value.legacy.batteryChargeCounter;
- break;
- case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
- pulledValue = value.legacy.batteryFullCharge;
- break;
- case FrameworkStatsLog.BATTERY_VOLTAGE:
- pulledValue = value.legacy.batteryVoltage;
- break;
- case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
- pulledValue = value.legacy.batteryCycleCount;
- break;
- default:
- throw new IllegalStateException("Invalid atomTag in healthHal puller: "
- + atomTag);
- }
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
- });
+ healthInfo = mHealthService.getHealthInfo();
} catch (RemoteException | IllegalStateException e) {
return StatsManager.PULL_SKIP;
}
+ if (healthInfo == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ int pulledValue;
+ switch (atomTag) {
+ case FrameworkStatsLog.BATTERY_LEVEL:
+ pulledValue = healthInfo.batteryLevel;
+ break;
+ case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
+ pulledValue = healthInfo.batteryChargeCounterUah;
+ break;
+ case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
+ pulledValue = healthInfo.batteryFullChargeUah;
+ break;
+ case FrameworkStatsLog.BATTERY_VOLTAGE:
+ pulledValue = healthInfo.batteryVoltageMillivolts;
+ break;
+ case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
+ pulledValue = healthInfo.batteryCycleCount;
+ break;
+ default:
+ return StatsManager.PULL_SKIP;
+ }
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 3177fac413fc..043646041158 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -457,17 +457,18 @@ public class TunerResourceManagerService extends SystemService implements IBinde
if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
throw new RemoteException("lnbHandle can't be invalid");
}
- if (!checkClientExists(clientId)) {
- throw new RemoteException("Release lnb from unregistered client:" + clientId);
- }
- LnbResource lnb = getLnbResource(lnbHandle);
- if (lnb == null) {
- throw new RemoteException("Releasing lnb does not exist.");
- }
- if (lnb.getOwnerClientId() != clientId) {
- throw new RemoteException("Client is not the current owner of the releasing lnb.");
- }
synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release lnb from unregistered client:" + clientId);
+ }
+ LnbResource lnb = getLnbResource(lnbHandle);
+ if (lnb == null) {
+ throw new RemoteException("Releasing lnb does not exist.");
+ }
+ if (lnb.getOwnerClientId() != clientId) {
+ throw new RemoteException("Client is not the current owner "
+ + "of the releasing lnb.");
+ }
releaseLnbInternal(lnb);
}
}
@@ -869,6 +870,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
ClientProfile requestClient = getClientProfile(request.clientId);
+ // TODO: check if this is really needed
if (requestClient == null) {
return false;
}
@@ -1205,7 +1207,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde
@Override
public void binderDied() {
synchronized (mLock) {
- removeClientProfile(mClientId);
+ if (checkClientExists(mClientId)) {
+ removeClientProfile(mClientId);
+ }
}
}
@@ -1246,6 +1250,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
// Reclaim all the resources of the share owners of the frontend that is used by the current
// resource reclaimed client.
ClientProfile profile = getClientProfile(reclaimingClientId);
+ // TODO: check if this check is really needed.
if (profile == null) {
return true;
}
@@ -1553,6 +1558,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
private void clearFrontendAndClientMapping(ClientProfile profile) {
+ // TODO: check if this check is really needed
if (profile == null) {
return;
}
@@ -1573,6 +1579,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+ // TODO: check if this check is really needed. Maybe needed for reclaimResource path.
if (profile == null) {
return;
}
diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
index 6bdb5cea7f9c..397acfac2812 100644
--- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
+++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
@@ -78,6 +78,14 @@ public final class TimingsTraceAndSlog extends TimingsTraceLog {
mTag = tag;
}
+ /**
+ * @see TimingsTraceLog#TimingsTraceLog(TimingsTraceLog)
+ */
+ public TimingsTraceAndSlog(@NonNull TimingsTraceAndSlog other) {
+ super(other);
+ this.mTag = other.mTag;
+ }
+
@Override
public void traceBegin(@NonNull String name) {
Slog.i(mTag, name);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 3d83995390f0..ec0b5f083a38 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -20,11 +20,11 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATIO
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
@@ -926,6 +926,8 @@ final class AccessibilityController {
final int windowType = windowState.mAttrs.type;
if (isExcludedWindowType(windowType)
|| ((windowState.mAttrs.privateFlags
+ & PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION) != 0)
+ || ((windowState.mAttrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
continue;
}
@@ -990,7 +992,6 @@ final class AccessibilityController {
}
}
}
-
visibleWindows.clear();
mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
@@ -1027,9 +1028,6 @@ final class AccessibilityController {
private boolean isExcludedWindowType(int windowType) {
return windowType == TYPE_MAGNIFICATION_OVERLAY
- // Omit the touch region to avoid the cut out of the magnification
- // bounds because nav bar panel is unmagnifiable.
- || windowType == TYPE_NAVIGATION_BAR_PANEL
// Omit the touch region of window magnification to avoid the cut out of the
// magnification and the magnified center of window magnification could be
// in the bounds
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 15db7909bfe8..2f9c1389f188 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -767,10 +767,6 @@ class ActivityMetricsLogger {
if (compatStateInfo.mLastLoggedActivity == r) {
compatStateInfo.mLastLoggedActivity = null;
}
- if (compatStateInfo.mVisibleActivities.isEmpty()) {
- // No need to keep the entry if there are no visible activities.
- mPackageUidToCompatStateInfo.remove(packageUid);
- }
}
/**
@@ -1266,13 +1262,14 @@ class ActivityMetricsLogger {
* activity.
* <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
* package UID and there are no other visible activities with the same package UID.
- * <li>The last logged activity with the same package UID is either {@code activity} or the
- * last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
+ * <li>The last logged activity with the same package UID is either {@code activity} (or an
+ * activity that has been removed) or the last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
* </ul>
*
* <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
- * activity} wasn't, looks for the first visible activity with the same package UID that has
- * a letterboxed state, or a non-letterboxed state if there isn't one, and logs that state.
+ * activity} (or an activity that has been removed) wasn't, looks for the first visible activity
+ * with the same package UID that has a letterboxed state, or a non-letterboxed state if
+ * there isn't one, and logs that state.
*
* <p>This method assumes that the caller is wrapping the call with a synchronized block so
* that there won't be a race condition between two activities with the same package.
@@ -1308,14 +1305,14 @@ class ActivityMetricsLogger {
if (!isVisible && !visibleActivities.isEmpty()) {
// There is another visible activity for this package UID.
- if (activity == lastLoggedActivity) {
+ if (lastLoggedActivity == null || activity == lastLoggedActivity) {
// Make sure a new visible state is logged if needed.
findAppCompatStateToLog(compatStateInfo, packageUid);
}
return;
}
- if (activity != lastLoggedActivity
+ if (lastLoggedActivity != null && activity != lastLoggedActivity
&& lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
&& lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
// Another visible activity for this package UID has logged a letterboxed state.
@@ -1329,15 +1326,25 @@ class ActivityMetricsLogger {
* Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
* state, or a non-letterboxed state if there isn't one, and logs that state for the given
* {@code packageUid}.
+ *
+ * <p>If there is a visible activity in {@code compatStateInfo} with the same state as the
+ * last logged state for the given {@code packageUid}, changes the last logged activity to
+ * reference the first such activity without actually logging the same state twice.
*/
private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+ final int lastLoggedState = compatStateInfo.mLastLoggedState;
ActivityRecord activityToLog = null;
int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
for (int i = 0; i < visibleActivities.size(); i++) {
ActivityRecord activity = visibleActivities.get(i);
int state = activity.getAppCompatState();
+ if (state == lastLoggedState) {
+ // Change last logged activity without logging the same state twice.
+ compatStateInfo.mLastLoggedActivity = activity;
+ return;
+ }
if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
// This shouldn't happen.
Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 535a061ee4ab..f94777339fae 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -934,6 +934,10 @@ public class AppTransitionController {
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
+ final RecentsAnimationController rac = mService.getRecentsAnimationController();
+ if (rac != null) {
+ rac.sendTasksAppeared();
+ }
for (int i = 0; i < openingApps.size(); ++i) {
openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b6552cb1b962..427bbeb78fb0 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -27,6 +27,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
+import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATION;
import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
import static com.android.server.wm.DisplayRotationProto.ROTATION;
import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
@@ -1527,6 +1528,7 @@ public class DisplayRotation {
proto.write(USER_ROTATION, getUserRotation());
proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation);
proto.write(LAST_ORIENTATION, mLastOrientation);
+ proto.write(IS_FIXED_TO_USER_ROTATION, isFixedToUserRotation());
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 5145e8eaaadd..3de98f18dae8 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.app.UiModeManager.MODE_NIGHT_AUTO;
-import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
-
import android.annotation.NonNull;
+import android.content.res.Configuration;
import android.os.Environment;
import android.os.LocaleList;
import android.util.AtomicFile;
@@ -308,7 +306,7 @@ public class PackageConfigPersister {
}
boolean isResetNightMode() {
- return mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM;
+ return mNightMode == Configuration.UI_MODE_NIGHT_UNDEFINED;
}
@Override
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b4963c5b9f1c..b54208d11974 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -173,10 +172,8 @@ class PinnedTaskController {
* to avoid flickering when running PiP animation across different orientations.
*/
void deferOrientationChangeForEnteringPipFromFullScreenIfNeeded() {
- final Task topFullscreenTask = mDisplayContent.getDefaultTaskDisplayArea()
- .getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityRecord topFullscreen = topFullscreenTask != null
- ? topFullscreenTask.topRunningActivity() : null;
+ final ActivityRecord topFullscreen = mDisplayContent.getActivity(
+ a -> a.fillsParent() && !a.getTask().inMultiWindowMode());
if (topFullscreen == null || topFullscreen.hasFixedRotationTransform()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 634f4894f3ee..38e3e3a82cb6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -162,6 +163,8 @@ public class RecentsAnimationController implements DeathRecipient {
private boolean mNavigationBarAttachedToApp;
private ActivityRecord mNavBarAttachedApp;
+ private final ArrayList<RemoteAnimationTarget> mPendingTaskAppears = new ArrayList<>();
+
/**
* An app transition listener to cancel the recents animation only after the app transition
* starts or is canceled.
@@ -731,11 +734,19 @@ public class RecentsAnimationController implements DeathRecipient {
return;
}
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addTaskToTargets, target: %s", target);
- try {
- mRunner.onTaskAppeared(target);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to report task appeared", e);
- }
+ mPendingTaskAppears.add(target);
+ }
+ }
+
+ void sendTasksAppeared() {
+ if (mPendingTaskAppears.isEmpty() || mRunner == null) return;
+ try {
+ final RemoteAnimationTarget[] targets = mPendingTaskAppears.toArray(
+ new RemoteAnimationTarget[0]);
+ mRunner.onTasksAppeared(targets);
+ mPendingTaskAppears.clear();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report task appeared", e);
}
}
@@ -743,10 +754,15 @@ public class RecentsAnimationController implements DeathRecipient {
OnAnimationFinishedCallback finishedCallback) {
final SparseBooleanArray recentTaskIds =
mService.mAtmService.getRecentTasks().getRecentTaskIds();
+ // The target must be built off the root task (the leaf task surface would be cropped
+ // within the root surface). However, recents only tracks leaf task ids, so we'll replace
+ // the task-id with the leaf id.
+ final Task leafTask = task.getTopLeafTask();
+ int taskId = leafTask.mTaskId;
TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task,
- !recentTaskIds.get(task.mTaskId), true /* hidden */, finishedCallback);
- mPendingNewTaskTargets.add(task.mTaskId);
- return adapter.createRemoteAnimationTarget();
+ !recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
+ mPendingNewTaskTargets.add(taskId);
+ return adapter.createRemoteAnimationTarget(taskId);
}
void logRecentsAnimationStartTime(int durationMs) {
@@ -781,7 +797,8 @@ public class RecentsAnimationController implements DeathRecipient {
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
- final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationTarget();
+ final RemoteAnimationTarget target =
+ taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID);
if (target != null) {
targets.add(target);
} else {
@@ -994,6 +1011,8 @@ public class RecentsAnimationController implements DeathRecipient {
removeAnimation(taskAdapter);
taskAdapter.onCleanup();
}
+ // Should already be empty, but clean-up pending task-appears in-case they weren't sent.
+ mPendingTaskAppears.clear();
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i);
@@ -1217,7 +1236,14 @@ public class RecentsAnimationController implements DeathRecipient {
mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
}
- RemoteAnimationTarget createRemoteAnimationTarget() {
+ /**
+ * @param overrideTaskId overrides the target's taskId. It may differ from mTaskId and thus
+ * can differ from taskInfo. This mismatch is needed, however, in
+ * some cases where we are animating root tasks but need need leaf
+ * ids for identification. If this is INVALID (-1), then mTaskId
+ * will be used.
+ */
+ RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId) {
final ActivityRecord topApp = mTask.getTopVisibleActivity();
final WindowState mainWindow = topApp != null
? topApp.findMainWindow()
@@ -1231,7 +1257,10 @@ public class RecentsAnimationController implements DeathRecipient {
final int mode = topApp.getActivityType() == mTargetActivityType
? MODE_OPENING
: MODE_CLOSING;
- mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
+ if (overrideTaskId < 0) {
+ overrideTaskId = mTask.mTaskId;
+ }
+ mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash,
!topApp.fillsParent(), new Rect(),
insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
mLocalBounds, mBounds, mTask.getWindowConfiguration(),
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 367feac18975..4a8f36273a33 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2366,6 +2366,16 @@ class Task extends TaskFragment {
return true;
}
+ /** Return the top-most leaf-task under this one, or this task if it is a leaf. */
+ public Task getTopLeafTask() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final Task child = mChildren.get(i).asTask();
+ if (child == null) continue;
+ return child.getTopLeafTask();
+ }
+ return this;
+ }
+
int getDescendantTaskCount() {
final int[] currentCount = {0};
final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 49bbd8a60a3f..0eaa25be3094 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -31,6 +31,7 @@ import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
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.os.Process.INVALID_UID;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -220,6 +221,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/** Organizer that organizing this TaskFragment. */
@Nullable
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+ private int mTaskFragmentOrganizerUid = INVALID_UID;
+ private @Nullable String mTaskFragmentOrganizerProcessName;
/** Client assigned unique token for this TaskFragment if this is created by an organizer. */
@Nullable
@@ -232,13 +235,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
*/
private boolean mDelayLastActivityRemoval;
- /**
- * The PID of the organizer that created this TaskFragment. It should be the same as the PID
- * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
- * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment.
- */
- private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;
-
final Point mLastSurfaceSize = new Point();
private final Rect mTmpInsets = new Rect();
@@ -334,9 +330,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mDelayLastActivityRemoval = false;
}
- void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
+ void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
+ @NonNull String processName) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
- mTaskFragmentOrganizerPid = pid;
+ mTaskFragmentOrganizerUid = uid;
+ mTaskFragmentOrganizerProcessName = processName;
}
/** Whether this TaskFragment is organized by the given {@code organizer}. */
@@ -2176,9 +2174,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
List<IBinder> childActivities = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
WindowContainer wc = getChildAt(i);
- if (mTaskFragmentOrganizerPid != ActivityRecord.INVALID_PID
+ if (mTaskFragmentOrganizerUid != INVALID_UID
&& wc.asActivityRecord() != null
- && wc.asActivityRecord().getPid() == mTaskFragmentOrganizerPid) {
+ && wc.asActivityRecord().info.processName.equals(
+ mTaskFragmentOrganizerProcessName)
+ && wc.asActivityRecord().getUid() == mTaskFragmentOrganizerUid) {
// Only includes Activities that belong to the organizer process for security.
childActivities.add(wc.asActivityRecord().token);
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f6c835640a2a..3d479d1e0d68 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -580,27 +580,130 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
break;
}
- case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: {
effects |= setAdjacentRootsHierarchyOp(hop);
break;
- }
- // The following operations may change task order so they are skipped while in lock task
- // mode. The above operations are still allowed because they don't move tasks. And it may
- // be necessary such as clearing launch root after entering lock task mode.
- if (isInLockTaskMode) {
- Slog.w(TAG, "Skip applying hierarchy operation " + hop + " while in lock task mode");
- return effects;
+ }
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
+ final TaskFragmentCreationParams taskFragmentCreationOptions =
+ hop.getTaskFragmentCreationOptions();
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
+ break;
+ }
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment == null || taskFragment.asTask() != null) {
+ throw new IllegalArgumentException(
+ "Can only delete organized TaskFragment, but not Task.");
+ }
+ if (isInLockTaskMode) {
+ final ActivityRecord bottomActivity = taskFragment.getActivity(
+ a -> !a.finishing, false /* traverseTopToBottom */);
+ if (bottomActivity != null
+ && mService.getLockTaskController().activityBlockedFromFinish(
+ bottomActivity)) {
+ Slog.w(TAG, "Skip removing TaskFragment due in lock task mode.");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
+ new IllegalStateException(
+ "Not allow to delete task fragment in lock task mode."));
+ break;
+ }
+ }
+ effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
+ final IBinder fragmentToken = hop.getContainer();
+ if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ final Intent activityIntent = hop.getActivityIntent();
+ final Bundle activityOptions = hop.getLaunchOptions();
+ final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
+ final int result = mService.getActivityStartController()
+ .startActivityInTaskFragment(tf, activityIntent, activityOptions,
+ hop.getCallingActivity());
+ if (!isStartResultSuccessful(result)) {
+ sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
+ errorCallbackToken,
+ convertStartFailureToThrowable(result, activityIntent));
+ }
+ break;
+ }
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
+ final IBinder fragmentToken = hop.getNewParent();
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
+ if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token or activity.");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ break;
+ }
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: {
+ final IBinder fragmentToken = hop.getContainer();
+ final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
+ final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
+ final TaskFragment tf2 = adjacentFragmentToken != null
+ ? mLaunchTaskFragments.get(adjacentFragmentToken)
+ : null;
+ if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to set adjacent on invalid fragment tokens");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ tf1.setAdjacentTaskFragment(tf2);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+
+ final Bundle bundle = hop.getLaunchOptions();
+ final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
+ bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
+ bundle) : null;
+ if (adjacentParams == null) {
+ break;
+ }
+
+ tf1.setDelayLastActivityRemoval(
+ adjacentParams.shouldDelayPrimaryLastActivityRemoval());
+ if (tf2 != null) {
+ tf2.setDelayLastActivityRemoval(
+ adjacentParams.shouldDelaySecondaryLastActivityRemoval());
+ }
+ break;
+ }
+ default: {
+ // The other operations may change task order so they are skipped while in lock
+ // task mode. The above operations are still allowed because they don't move
+ // tasks. And it may be necessary such as clearing launch root after entering
+ // lock task mode.
+ if (isInLockTaskMode) {
+ Slog.w(TAG, "Skip applying hierarchy operation " + hop
+ + " while in lock task mode");
+ return effects;
+ }
+ }
}
- final WindowContainer wc;
- final IBinder fragmentToken;
switch (type) {
- case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
break;
+ }
case HIERARCHY_OP_TYPE_REORDER:
- case HIERARCHY_OP_TYPE_REPARENT:
- wc = WindowContainer.fromBinder(hop.getContainer());
+ case HIERARCHY_OP_TYPE_REPARENT: {
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
if (wc == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
break;
@@ -629,7 +732,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
- case HIERARCHY_OP_TYPE_LAUNCH_TASK:
+ }
+ case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"launchTask HierarchyOp");
final Bundle launchOpts = hop.getLaunchOptions();
@@ -638,7 +742,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
final SafeActivityOptions safeOptions =
SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
- final Integer[] starterResult = { null };
+ final Integer[] starterResult = {null};
// startActivityFromRecents should not be called in lock.
mService.mH.post(() -> {
try {
@@ -659,10 +763,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
break;
- case HIERARCHY_OP_TYPE_PENDING_INTENT:
+ }
+ case HIERARCHY_OP_TYPE_PENDING_INTENT: {
String resolvedType = hop.getActivityIntent() != null
? hop.getActivityIntent().resolveTypeIfNeeded(
- mService.mContext.getContentResolver())
+ mService.mContext.getContentResolver())
: null;
Bundle options = null;
@@ -682,57 +787,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
null /* requiredPermission */, options);
break;
- case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
- final TaskFragmentCreationParams taskFragmentCreationOptions =
- hop.getTaskFragmentCreationOptions();
- createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
- break;
- case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
- wc = WindowContainer.fromBinder(hop.getContainer());
- if (wc == null || !wc.isAttached()) {
- Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
- break;
- }
- final TaskFragment taskFragment = wc.asTaskFragment();
- if (taskFragment == null || taskFragment.asTask() != null) {
- throw new IllegalArgumentException(
- "Can only delete organized TaskFragment, but not Task.");
- }
- effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
- break;
- case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
- fragmentToken = hop.getContainer();
- if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
- final Throwable exception = new IllegalArgumentException(
- "Not allowed to operate with invalid fragment token");
- sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
- break;
- }
- final Intent activityIntent = hop.getActivityIntent();
- final Bundle activityOptions = hop.getLaunchOptions();
- final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
- final int result = mService.getActivityStartController()
- .startActivityInTaskFragment(tf, activityIntent, activityOptions,
- hop.getCallingActivity());
- if (!isStartResultSuccessful(result)) {
- sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
- errorCallbackToken,
- convertStartFailureToThrowable(result, activityIntent));
- }
- break;
- case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
- fragmentToken = hop.getNewParent();
- final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
- if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
- final Throwable exception = new IllegalArgumentException(
- "Not allowed to operate with invalid fragment token or activity.");
- sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
- break;
- }
- activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
- break;
- case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ }
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
final WindowContainer newParent = hop.getNewParent() != null
? WindowContainer.fromBinder(hop.getNewParent())
@@ -745,37 +801,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
reparentTaskFragment(oldParent, newParent, errorCallbackToken);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
- case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
- fragmentToken = hop.getContainer();
- final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
- final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
- final TaskFragment tf2 = adjacentFragmentToken != null
- ? mLaunchTaskFragments.get(adjacentFragmentToken)
- : null;
- if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
- final Throwable exception = new IllegalArgumentException(
- "Not allowed to set adjacent on invalid fragment tokens");
- sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
- break;
- }
- tf1.setAdjacentTaskFragment(tf2);
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
-
- final Bundle bundle = hop.getLaunchOptions();
- final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
- bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
- bundle) : null;
- if (adjacentParams == null) {
- break;
- }
-
- tf1.setDelayLastActivityRemoval(
- adjacentParams.shouldDelayPrimaryLastActivityRemoval());
- if (tf2 != null) {
- tf2.setDelayLastActivityRemoval(
- adjacentParams.shouldDelaySecondaryLastActivityRemoval());
- }
- break;
+ }
}
return effects;
}
@@ -1204,8 +1230,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
creationParams.getFragmentToken(), true /* createdByOrganizer */);
// Set task fragment organizer immediately, since it might have to be notified about further
// actions.
- taskFragment.setTaskFragmentOrganizer(
- creationParams.getOrganizer(), ownerActivity.getPid());
+ taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
+ ownerActivity.getUid(), ownerActivity.info.processName);
ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
taskFragment.setBounds(creationParams.getInitialBounds());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 92c0a34dc382..ebfdfa3eb5da 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12747,6 +12747,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public @Nullable ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(
+ @NonNull UserHandle userHandle) {
+ return DevicePolicyManagerService.this.getProfileOwnerOrDeviceOwnerSupervisionComponent(
+ userHandle);
+ }
+
+ @Override
public boolean isActiveDeviceOwner(int uid) {
return isDeviceOwner(new CallerIdentity(uid, null, null));
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b44b692bce12..752ad0b9c48a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1357,6 +1357,7 @@ public final class SystemServer implements Dumpable {
*/
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("startOtherServices");
+ mSystemServiceManager.updateOtherServicesStartIndex();
final Context context = mSystemContext;
DynamicSystemService dynamicSystem = null;
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 3ffce8c426a4..ad79c65691c3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -624,6 +624,60 @@ class DomainVerificationPackageTest {
}
@Test
+ fun migratePackageSelected() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE,
+ listOf(DOMAIN_1), listOf(DOMAIN_2))
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO,
+ listOf(DOMAIN_1), listOf(DOMAIN_2))
+
+ val map = mutableMapOf<String, PackageStateInternal>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ service.getInfo(pkgName).run {
+ assertThat(identifier).isEqualTo(UUID_ONE)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ ))
+ }
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ service.getInfo(pkgName).run {
+ assertThat(identifier).isEqualTo(UUID_TWO)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ ))
+ }
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
fun backupAndRestore() {
// This test acts as a proxy for true user restore through PackageManager,
// as that's much harder to test for real.
@@ -804,7 +858,8 @@ class DomainVerificationPackageTest {
pkgName: String,
domainSetId: UUID,
signature: String,
- domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
+ autoVerifyDomains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
+ otherDomains: List<String> = listOf(),
isSystemApp: Boolean = false
) = mockThrowOnUnmocked<PackageStateInternal> {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
@@ -812,23 +867,25 @@ class DomainVerificationPackageTest {
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
whenever(isEnabled) { true }
+ fun baseIntent(domain: String) = ParsedIntentInfoImpl().apply {
+ intentFilter.apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(domain, null)
+ }
+ }
+
val activityList = listOf(
ParsedActivityImpl().apply {
- domains.forEach {
- addIntent(
- ParsedIntentInfoImpl().apply {
- intentFilter.apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("http")
- addDataScheme("https")
- addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
- addDataAuthority(it, null)
- }
- }
- )
+ autoVerifyDomains.forEach {
+ addIntent(baseIntent(it).apply { intentFilter.autoVerify = true })
+ }
+ otherDomains.forEach {
+ addIntent(baseIntent(it).apply { intentFilter.autoVerify = false })
}
},
)
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 6bb127a1b3c5..48a8b1bca99b 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -29,7 +29,10 @@ package {
android_test {
name: "FrameworksMockingServicesTests",
- defaults: ["FrameworkMockingServicesTests-jni-defaults"],
+ defaults: [
+ "FrameworkMockingServicesTests-jni-defaults",
+ "modules-utils-testable-device-config-defaults",
+ ],
srcs: [
"src/**/*.java",
@@ -67,9 +70,7 @@ android_test {
],
jni_libs: [
- "libdexmakerjvmtiagent",
"libpsi",
- "libstaticjvmtiagent",
],
certificate: "platform",
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 61cd4448cc35..a9099ae35b75 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -621,12 +621,8 @@ public class AlarmManagerServiceTest {
}
private void setTareEnabled(boolean enabled) {
- doReturn(enabled ? 1 : 0).when(
- () -> Settings.Global.getInt(mContentResolver, Settings.Global.ENABLE_TARE));
- doReturn(enabled ? 1 : 0).when(
- () -> Settings.Global.getInt(mContentResolver,
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE));
- mService.mConstants.onChange(true);
+ when(mEconomyManagerInternal.isEnabled()).thenReturn(enabled);
+ mService.mConstants.onTareEnabledStateChanged(enabled);
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 609768c0e62a..766ba72d7cb7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -38,10 +38,10 @@ import android.provider.DeviceConfig;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.Before;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index a883293b13b9..303f95565c76 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -40,10 +40,10 @@ import android.text.TextUtils;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
index ad19a4893153..d747914caa9a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
@@ -29,7 +29,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index 007191f02631..349da03e3f4e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -59,7 +59,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IPlatformCompat;
-import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+import com.android.modules.utils.testing.TestableDeviceConfig.TestableDeviceConfigRule;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
index e093f1396385..9fac0c029815 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
@@ -34,7 +34,7 @@ import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import org.junit.Before;
import org.junit.ClassRule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
index f94377fe51c2..6a3548178cba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -36,7 +36,7 @@ import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import org.junit.Before;
import org.junit.ClassRule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java
deleted file mode 100644
index c0ab70a57327..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class MultipleStaticMocksTest {
- @Rule
- public StaticMockFixtureRule mStaticMockFixtureRule =
- new StaticMockFixtureRule(AB::new, CD::new);
-
- private List<String> mCollected;
-
- @Test
- public void testMultipleStaticMocks() throws Exception {
- mCollected = new ArrayList<>();
- int n = 0;
-
- A.a();
- n = verifyCollected(n, "A.a");
-
- D.b();
- n = verifyCollected(n, "D.b");
-
- C.b();
- n = verifyCollected(n, "C.b");
-
- B.a();
- n = verifyCollected(n, "B.a");
- }
-
- private int verifyCollected(int n, String... last) {
- assertThat(mCollected).hasSize(n + last.length);
- assertThat(mCollected.subList(n, mCollected.size()))
- .containsExactlyElementsIn(last).inOrder();
- return n + last.length;
- }
-
- private static class A {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class B {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class C {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class D {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
- */
- private class AB implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(A.class);
- sessionBuilder.spyStatic(B.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.a");
- return null;
- }).when(A::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.b");
- return null;
- }).when(A::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.a");
- return null;
- }).when(B::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.b");
- return null;
- }).when(B::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
- */
- private class CD implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(C.class);
- sessionBuilder.spyStatic(D.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.a");
- return null;
- }).when(C::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.b");
- return null;
- }).when(C::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.a");
- return null;
- }).when(D::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.b");
- return null;
- }).when(D::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java
deleted file mode 100644
index 0303fe1e1eb0..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-/**
- * Provides support for a set of static mocks for use within a single shared
- * {@link StaticMockitoSession}.
- */
-public interface StaticMockFixture {
- /**
- * Adds any required mock or spy classes managed by this {@link StaticMockFixture} to the
- * {@link StaticMockitoSessionBuilder} provided.
- *
- * Call this to set up the classes that this expects to be mocked, by adding them to the
- * {@link StaticMockitoSessionBuilder} using
- * {@link StaticMockitoSessionBuilder#mockStatic(Class)},
- * {@link StaticMockitoSessionBuilder#spyStatic(Class)} or similar as appropriate.
- *
- * @param sessionBuilder the {@link StaticMockitoSessionBuilder} to which the classes should be
- * added to mock, spy, or otherwise as required
- * @return sessionBuilder, to allow for fluent programming
- */
- StaticMockitoSessionBuilder setUpMockedClasses(StaticMockitoSessionBuilder sessionBuilder);
-
- /**
- * Configures the behaviours of any mock or spy classes managed by this
- * {@link StaticMockFixture}.
- *
- * Call this after {@link StaticMockitoSessionBuilder#startMocking()} has been called.
- * This sets up any default behaviors for the mocks, spys, etc.
- */
- void setUpMockBehaviors();
-
- /**
- * Tear everything down.
- */
- void tearDown();
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
deleted file mode 100644
index 3566aee2eba3..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Supplier;
-
-/**
- * <p>StaticMockFixtureRule is a {@link TestRule} that wraps one or more {@link StaticMockFixture}s
- * to set them up and tear it down automatically. This works well when you have no other static
- * mocks than the ones supported by their respective {@link StaticMockFixture}s.</p>
- *
- * <p>StaticMockFixtureRule should be defined as a rule on your test so it can clean up after
- * itself. Like the following:</p>
- * <pre class="prettyprint">
-* public final StaticMockFixture mStaticMockFixtures = ...;
- * &#064;Rule
- * public final StaticMockFixtureRule mStaticMockFixtureRule =
- * new StaticMockFixtureRule(mStaticMockFixtures);
- * </pre>
- */
-public class StaticMockFixtureRule implements TestRule {
- private StaticMockitoSession mMockitoSession;
- private StaticMockFixture[] mStaticMockFixtures;
- private Supplier<? extends StaticMockFixture>[] mSupplier;
-
- /**
- * Constructs a StaticMockFixtureRule that always uses the same {@link StaticMockFixture}
- * instance(s).
- *
- * @param staticMockFixtures the {@link StaticMockFixture}(s) to use.
- */
- public StaticMockFixtureRule(StaticMockFixture... staticMockFixtures) {
- mStaticMockFixtures = staticMockFixtures;
- mSupplier = null;
- }
-
- /**
- * Constructs a StaticMockFixtureRule that retrieves a new {@link StaticMockFixture} instance
- * from one or more {@link Supplier<? extends StaticMockFixture >}s for each test invocation.
- *
- * @param supplier the {@link Supplier<? extends StaticMockFixture >}(s) that will supply the
- * {@link StaticMockFixture}(s).
- */
- @SafeVarargs
- public StaticMockFixtureRule(Supplier<? extends StaticMockFixture>... supplier) {
- mStaticMockFixtures = null;
- mSupplier = supplier;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- StaticMockitoSessionBuilder sessionBuilder = getSessionBuilder();
-
- if (mSupplier != null) {
- mStaticMockFixtures = new StaticMockFixture[mSupplier.length];
- for (int i = 0; i < mSupplier.length; i++) {
- mStaticMockFixtures[i] = mSupplier[i].get();
- }
- }
-
- for (int i = 0; i < mStaticMockFixtures.length; i++) {
- sessionBuilder = mStaticMockFixtures[i].setUpMockedClasses(sessionBuilder);
- }
-
- mMockitoSession = sessionBuilder.startMocking();
-
- for (int i = 0; i < mStaticMockFixtures.length; i++) {
- mStaticMockFixtures[i].setUpMockBehaviors();
- }
-
- return new TestWatcher() {
- @Override
- protected void succeeded(Description description) {
- tearDown(null);
- }
-
- @Override
- protected void skipped(AssumptionViolatedException e, Description description) {
- tearDown(e);
- }
-
- @Override
- protected void failed(Throwable e, Description description) {
- tearDown(e);
- }
- }.apply(base, description);
- }
-
- /**
- * This allows overriding the creation of the builder for a new {@link StaticMockitoSession}.
- * Mainly for testing, but also useful if you have other requirements for the session.
- *
- * @return a new {@link StaticMockitoSessionBuilder}.
- */
- public StaticMockitoSessionBuilder getSessionBuilder() {
- return mockitoSession().strictness(Strictness.LENIENT);
- }
-
- private void tearDown(Throwable e) {
- mMockitoSession.finishMocking(e);
-
- for (int i = mStaticMockFixtures.length - 1; i >= 0; i--) {
- mStaticMockFixtures[i].tearDown();
- if (mSupplier != null) {
- mStaticMockFixtures[i] = null;
- }
- }
-
- if (mSupplier != null) {
- mStaticMockFixtures = null;
- }
-
- mMockitoSession = null;
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
deleted file mode 100644
index 8e0ccf01d7a7..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.quality.Strictness.LENIENT;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.After;
-import org.junit.AssumptionViolatedException;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoSession;
-
-import java.util.function.Supplier;
-
-/** Tests that StaticMockFixture manages fixtures and suppliers correctly. */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class StaticMockFixtureRuleTest {
- private MockitoSession mMockitoSession;
-
- @Mock private StaticMockitoSessionBuilder mSessionBuilder;
- @Mock private StaticMockitoSession mSession;
- @Mock private StaticMockFixture mA1;
- @Mock private StaticMockFixture mB1;
- @Mock private StaticMockFixture mA2;
- @Mock private StaticMockFixture mB2;
- @Mock private Supplier<StaticMockFixture> mSupplyA;
- @Mock private Supplier<StaticMockFixture> mSupplyB;
- @Mock private Statement mStatement;
- @Mock private Statement mSkipStatement;
- @Mock private Statement mThrowStatement;
- @Mock private Description mDescription;
-
- @Before
- public void setUp() throws Throwable {
- mMockitoSession = Mockito.mockitoSession()
- .strictness(LENIENT)
- .initMocks(this)
- .startMocking();
- prepareMockBehaviours();
- }
-
- @After
- public void tearDown() {
- mMockitoSession.finishMocking();
- }
-
- private void prepareFixtureMocks(StaticMockFixture... mocks) {
- for (StaticMockFixture mock : mocks) {
- when(mock.setUpMockedClasses(any())).thenAnswer(
- invocation -> invocation.getArgument(0));
- doNothing().when(mock).setUpMockBehaviors();
- }
- }
-
- private void prepareMockBehaviours() throws Throwable {
- when(mSessionBuilder.startMocking()).thenReturn(mSession);
- when(mSupplyA.get()).thenReturn(mA1, mA2);
- when(mSupplyB.get()).thenReturn(mB1, mB2);
- prepareFixtureMocks(mA1, mA2, mB1, mB2);
- when(mA1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
- doNothing().when(mA1).setUpMockBehaviors();
- when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
- doNothing().when(mB1).setUpMockBehaviors();
- doNothing().when(mStatement).evaluate();
- doThrow(new AssumptionViolatedException("bad assumption, test should be skipped"))
- .when(mSkipStatement).evaluate();
- doThrow(new IllegalArgumentException("bad argument, test should be failed"))
- .when(mThrowStatement).evaluate();
- doNothing().when(mA1).tearDown();
- doNothing().when(mB1).tearDown();
- }
-
- private InOrder mocksInOrder() {
- return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, mA1, mA2, mB1, mB2,
- mStatement, mSkipStatement, mThrowStatement, mDescription);
- }
-
- private void verifyNoMoreImportantMockInteractions() {
- verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement,
- mSkipStatement, mThrowStatement);
- }
-
- @Test
- public void testRuleWorksWithExplicitFixtures() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement runMe = rule.apply(mStatement, mDescription);
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- runMe.evaluate();
-
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- // Round two: use the same fixtures again.
- rule.apply(mStatement, mDescription).evaluate();
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-
- @Test
- public void testRuleWorksWithFixtureSuppliers() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mSupplyA, mSupplyB) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement runMe = rule.apply(mStatement, mDescription);
-
- inOrder.verify(mSupplyA).get();
- inOrder.verify(mSupplyB).get();
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- runMe.evaluate();
-
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- // Round two: use the same suppliers again to retrieve different fixtures: mA2 and mB2
- rule.apply(mStatement, mDescription).evaluate();
-
- inOrder.verify(mSupplyA).get();
- inOrder.verify(mSupplyB).get();
- inOrder.verify(mA2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA2).setUpMockBehaviors();
- inOrder.verify(mB2).setUpMockBehaviors();
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB2).tearDown();
- inOrder.verify(mA2).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-
- @Test
- public void testTearDownOnSkippedTests() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement skipStatement = rule.apply(mSkipStatement, mDescription);
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- try {
- skipStatement.evaluate();
- fail("AssumptionViolatedException should have been thrown");
- } catch (AssumptionViolatedException e) {
- // expected
- }
-
- inOrder.verify(mSkipStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-
- @Test
- public void testTearDownOnFailedTests() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement failStatement = rule.apply(mThrowStatement, mDescription);
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- try {
- failStatement.evaluate();
- fail("IllegalArgumentException should have been thrown");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- inOrder.verify(mThrowStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
deleted file mode 100644
index 60a7f78c6949..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.when;
-
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
-import android.util.ArrayMap;
-import android.util.Pair;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.rules.TestRule;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
-
-/**
- * TestableDeviceConfig is a {@link StaticMockFixture} that uses ExtendedMockito to replace the real
- * implementation of DeviceConfig with essentially a local HashMap in the callers process. This
- * allows for unit testing that do not modify the real DeviceConfig on the device at all.
- */
-public final class TestableDeviceConfig implements StaticMockFixture {
-
- private Map<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>>
- mOnPropertiesChangedListenerMap = new HashMap<>();
- private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
-
- /**
- * Clears out all local overrides.
- */
- public void clearDeviceConfig() {
- mKeyValueMap.clear();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(DeviceConfig.class);
- return sessionBuilder;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setUpMockBehaviors() {
- doAnswer((Answer<Void>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- Executor executor = invocationOnMock.getArgument(1);
- DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener =
- invocationOnMock.getArgument(2);
- mOnPropertiesChangedListenerMap.put(
- onPropertiesChangedListener, new Pair<>(namespace, executor));
- return null;
- }).when(() -> DeviceConfig.addOnPropertiesChangedListener(
- anyString(), any(Executor.class),
- any(DeviceConfig.OnPropertiesChangedListener.class)));
-
- doAnswer((Answer<Boolean>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- String name = invocationOnMock.getArgument(1);
- String value = invocationOnMock.getArgument(2);
- mKeyValueMap.put(getKey(namespace, name), value);
- invokeListeners(namespace, getProperties(namespace, name, value));
- return true;
- }
- ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
-
- doAnswer((Answer<Boolean>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- String name = invocationOnMock.getArgument(1);
- mKeyValueMap.remove(getKey(namespace, name));
- invokeListeners(namespace, getProperties(namespace, name, null));
- return true;
- }
- ).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
-
- doAnswer((Answer<Boolean>) invocationOnMock -> {
- Properties properties = invocationOnMock.getArgument(0);
- String namespace = properties.getNamespace();
- Map<String, String> keyValues = new ArrayMap<>();
- for (String name : properties.getKeyset()) {
- String value = properties.getString(name, /* defaultValue= */ "");
- mKeyValueMap.put(getKey(namespace, name), value);
- keyValues.put(name.toLowerCase(), value);
- }
- invokeListeners(namespace, getProperties(namespace, keyValues));
- return true;
- }
- ).when(() -> DeviceConfig.setProperties(any(Properties.class)));
-
- doAnswer((Answer<String>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- String name = invocationOnMock.getArgument(1);
- return mKeyValueMap.get(getKey(namespace, name));
- }).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
-
- doAnswer((Answer<Properties>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- final int varargStartIdx = 1;
- Map<String, String> keyValues = new ArrayMap<>();
- if (invocationOnMock.getArguments().length == varargStartIdx) {
- mKeyValueMap.entrySet().forEach(entry -> {
- Pair<String, String> nameSpaceAndName = getNameSpaceAndName(entry.getKey());
- if (!nameSpaceAndName.first.equals(namespace)) {
- return;
- }
- keyValues.put(nameSpaceAndName.second.toLowerCase(), entry.getValue());
- });
- } else {
- for (int i = varargStartIdx; i < invocationOnMock.getArguments().length; ++i) {
- String name = invocationOnMock.getArgument(i);
- keyValues.put(name.toLowerCase(), mKeyValueMap.get(getKey(namespace, name)));
- }
- }
- return getProperties(namespace, keyValues);
- }).when(() -> DeviceConfig.getProperties(anyString(), ArgumentMatchers.<String>any()));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void tearDown() {
- clearDeviceConfig();
- mOnPropertiesChangedListenerMap.clear();
- }
-
- private static String getKey(String namespace, String name) {
- return namespace + "/" + name;
- }
-
- private Pair<String, String> getNameSpaceAndName(String key) {
- final String[] values = key.split("/");
- return Pair.create(values[0], values[1]);
- }
-
- private void invokeListeners(String namespace, Properties properties) {
- for (DeviceConfig.OnPropertiesChangedListener listener :
- mOnPropertiesChangedListenerMap.keySet()) {
- if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
- mOnPropertiesChangedListenerMap.get(listener).second.execute(
- () -> listener.onPropertiesChanged(properties));
- }
- }
- }
-
- private Properties getProperties(String namespace, String name, String value) {
- return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
- }
-
- private Properties getProperties(String namespace, Map<String, String> keyValues) {
- Properties properties = Mockito.mock(Properties.class);
- when(properties.getNamespace()).thenReturn(namespace);
- when(properties.getKeyset()).thenReturn(keyValues.keySet());
- when(properties.getBoolean(anyString(), anyBoolean())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- boolean defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- return Boolean.parseBoolean(value);
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getFloat(anyString(), anyFloat())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- float defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- try {
- return Float.parseFloat(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getInt(anyString(), anyInt())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- int defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getLong(anyString(), anyLong())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- long defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getString(anyString(), nullable(String.class))).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- String defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- return value;
- } else {
- return defaultValue;
- }
- }
- );
-
- return properties;
- }
-
- /**
- * <p>TestableDeviceConfigRule is a {@link TestRule} that wraps a {@link TestableDeviceConfig}
- * to set it up and tear it down automatically. This works well when you have no other static
- * mocks.</p>
- *
- * <p>TestableDeviceConfigRule should be defined as a rule on your test so it can clean up after
- * itself. Like the following:</p>
- * <pre class="prettyprint">
- * &#064;Rule
- * public final TestableDeviceConfigRule mTestableDeviceConfigRule =
- * new TestableDeviceConfigRule();
- * </pre>
- */
- public static class TestableDeviceConfigRule extends StaticMockFixtureRule {
- public TestableDeviceConfigRule() {
- super(TestableDeviceConfig::new);
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java
deleted file mode 100644
index 9616d13e42fe..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-import android.provider.DeviceConfig;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TestableDeviceConfigAndOtherStaticMocksTest {
- @Rule
- public StaticMockFixtureRule mStaticMockFixtureRule =
- new StaticMockFixtureRule(TestableDeviceConfig::new, AB::new, CD::new);
-
- private List<String> mCollected;
-
- @Test
- public void testDeviceConfigAndOtherStaticMocks() throws Exception {
- mCollected = new ArrayList<>();
- int n = 0;
-
- String namespace = "foo";
- String flag = "bar";
- String flagValue = "new value";
-
- Assert.assertNull(DeviceConfig.getProperty(namespace, flag));
-
- A.a();
- verifyCollected(++n, "A.a");
-
- DeviceConfig.setProperty(namespace, flag, flagValue, false);
-
- D.b();
- verifyCollected(++n, "D.b");
-
- Assert.assertEquals(flagValue, DeviceConfig.getProperty(namespace, flag));
-
- C.b();
- verifyCollected(++n, "C.b");
-
- B.a();
- verifyCollected(++n, "B.a");
- }
-
- private void verifyCollected(int n, String last) {
- Assert.assertEquals(n, mCollected.size());
- Assert.assertEquals(last, mCollected.get(n - 1));
- }
-
- private static class A {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class B {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class C {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class D {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
- */
- private class AB implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(A.class);
- sessionBuilder.spyStatic(B.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.a");
- return null;
- }).when(A::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.b");
- return null;
- }).when(A::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.a");
- return null;
- }).when(B::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.b");
- return null;
- }).when(B::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
- */
- private class CD implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(C.class);
- sessionBuilder.spyStatic(D.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.a");
- return null;
- }).when(C::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.b");
- return null;
- }).when(C::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.a");
- return null;
- }).when(D::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.b");
- return null;
- }).when(D::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
deleted file mode 100644
index f9f43876f39a..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.testables;
-
-import static android.provider.DeviceConfig.OnPropertiesChangedListener;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.ActivityThread;
-import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.BadConfigException;
-import android.provider.DeviceConfig.Properties;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/** Tests that ensure appropriate settings are backed up. */
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TestableDeviceConfigTest {
- private static final String sNamespace = "namespace1";
- private static final String sKey = "key1";
- private static final String sValue = "value1";
- private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
-
- @Rule
- public TestableDeviceConfig.TestableDeviceConfigRule
- mTestableDeviceConfig = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Test
- public void getProperty_empty() {
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isNull();
- }
-
- @Test
- public void setAndGetProperty_sameNamespace() {
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(sValue);
- }
-
- @Test
- public void setAndGetProperty_differentNamespace() {
- String newNamespace = "namespace2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- String result = DeviceConfig.getProperty(newNamespace, sKey);
- assertThat(result).isNull();
- }
-
- @Test
- public void setAndGetProperty_multipleNamespaces() {
- String newNamespace = "namespace2";
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(sValue);
- result = DeviceConfig.getProperty(newNamespace, sKey);
- assertThat(result).isEqualTo(newValue);
- }
-
- @Test
- public void setAndGetProperty_overrideValue() {
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(newValue);
- }
-
- @Test
- public void setProperties() throws BadConfigException {
- String newKey = "key2";
- String newValue = "value2";
- DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
- sValue).setString(newKey, newValue).build());
- assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
- assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
- }
-
- @Test
- public void deleteProperty() {
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
- DeviceConfig.deleteProperty(sNamespace, sKey);
- assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
- String newNamespace = "namespace2";
- String newValue = "value2";
- DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
- assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
- DeviceConfig.deleteProperty(newNamespace, sKey);
- assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
- }
-
- @Test
- public void getProperties_empty() {
- String newKey = "key2";
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- Properties properties = DeviceConfig.getProperties(sNamespace);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
- assertThat(properties.getString(newKey, null)).isNull();
-
- DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
- properties = DeviceConfig.getProperties(sNamespace);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
- assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-
- }
-
- @Test
- public void getProperties() {
- Properties properties = DeviceConfig.getProperties(sNamespace, sKey);
- assertThat(properties.getString(sKey, null)).isNull();
-
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- properties = DeviceConfig.getProperties(sNamespace, sKey);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
-
- String newKey = "key2";
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
- properties = DeviceConfig.getProperties(sNamespace, sKey, newKey);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
- assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-
- String unsetKey = "key3";
- properties = DeviceConfig.getProperties(sNamespace, newKey, unsetKey);
- assertThat(properties.getKeyset()).containsExactly(newKey, unsetKey);
- assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
- assertThat(properties.getString(unsetKey, null)).isNull();
- }
-
- @Test
- public void testListener_setProperty() throws InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(1);
-
- OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).containsExactly(sKey);
- assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
- assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
- countDownLatch.countDown();
- };
- try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
- ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- assertThat(countDownLatch.await(
- WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(changeListener);
- }
- }
-
- @Test
- public void testListener_setProperties() throws BadConfigException, InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(1);
- String newKey = "key2";
- String newValue = "value2";
-
- OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
- assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
- assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
- assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
- countDownLatch.countDown();
- };
- try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
- ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
- sValue).setString(newKey, newValue).build());
- assertThat(countDownLatch.await(
- WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(changeListener);
- }
- }
-
- @Test
- public void testListener_deleteProperty() throws InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(1);
-
- OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).containsExactly(sKey);
- assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
- assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
- countDownLatch.countDown();
- };
- try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
- ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.deleteProperty(sNamespace, sKey);
- assertThat(countDownLatch.await(
- WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(changeListener);
- }
- }
-}
-
-
diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
index 1542b01be8e1..d74ff8f0b209 100644
--- a/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/usage/UserUsageStatsServiceTest.java
@@ -83,7 +83,7 @@ public class UserUsageStatsServiceTest {
HashMap<String, Long> installedPkgs = new HashMap<>();
installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis());
- mService.init(System.currentTimeMillis(), installedPkgs);
+ mService.init(System.currentTimeMillis(), installedPkgs, true);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b1da890a8751..3ade9ff61735 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -16,13 +16,22 @@
package com.android.server.accessibility;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,18 +47,23 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import com.android.compatibility.common.util.TestUtils;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -57,20 +71,30 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
/**
* APCT tests for {@link AccessibilityManagerService}.
*/
-public class AccessibilityManagerServiceTest extends AndroidTestCase {
+public class AccessibilityManagerServiceTest {
private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
private static final int ACTION_ID = 20;
private static final String LABEL = "label";
private static final String INTENT_ACTION = "TESTACTION";
private static final String DESCRIPTION = "description";
private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
- InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION),
+ PendingIntent.FLAG_MUTABLE_UNAUDITED);
private static final RemoteAction TEST_ACTION = new RemoteAction(
Icon.createWithContentUri("content://test"),
LABEL,
@@ -79,11 +103,12 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
new AccessibilityAction(ACTION_ID, LABEL);
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1;
+
static final ComponentName COMPONENT_NAME = new ComponentName(
"com.android.server.accessibility", "AccessibilityManagerServiceTest");
static final int SERVICE_ID = 42;
- @Mock private Context mMockContext;
@Mock private AccessibilityServiceInfo mMockServiceInfo;
@Mock private ResolveInfo mMockResolveInfo;
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@@ -100,16 +125,19 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
@Mock private IAccessibilityServiceClient mMockServiceClient;
@Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
@Mock private MagnificationController mMockMagnificationController;
- @Mock private Resources mMockResources;
+ @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
- private AccessibilityUserState mUserState;
+ @Rule
+ public final TestableContext mTestableContext = new TestableContext(
+ getInstrumentation().getTargetContext(), null);
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
private AccessibilityServiceConnection mAccessibilityServiceConnection;
+ private AccessibilityInputFilter mInputFilter;
private AccessibilityManagerService mA11yms;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
@@ -120,43 +148,54 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.addService(
UserManagerInternal.class, mMockUserManagerInternal);
+ mInputFilter = Mockito.mock(FakeInputFilter.class);
when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
mMockWindowMagnificationMgr);
+ when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn(
+ mMockFullScreenMagnificationController);
when(mMockWindowManagerService.getAccessibilityController()).thenReturn(
mMockA11yController);
when(mMockA11yController.isAccessibilityTracingEnabled()).thenReturn(false);
when(mMockUserManagerInternal.isUserUnlockingOrUnlocked(anyInt())).thenReturn(true);
+
+ final ArrayList<Display> displays = new ArrayList<>();
+ final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(),
+ Display.DEFAULT_DISPLAY, new DisplayInfo(),
+ DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY,
+ new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ displays.add(defaultDisplay);
+ displays.add(testDisplay);
+ when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays);
+
mA11yms = new AccessibilityManagerService(
- InstrumentationRegistry.getContext(),
- mMockPackageManager,
- mMockSecurityPolicy,
- mMockSystemActionPerformer,
- mMockA11yWindowManager,
- mMockA11yDisplayListener,
- mMockMagnificationController);
-
- mMockResources = mock(Resources.class);
- when(mMockContext.getResources()).thenReturn(mMockResources);
-
- mUserState = new AccessibilityUserState(
- mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms);
- mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), mUserState);
+ mTestableContext,
+ mMockPackageManager,
+ mMockSecurityPolicy,
+ mMockSystemActionPerformer,
+ mMockA11yWindowManager,
+ mMockA11yDisplayListener,
+ mMockMagnificationController,
+ mInputFilter);
+
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ mA11yms.getCurrentUserIdLocked(), mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), userState);
}
private void setupAccessibilityServiceConnection() {
- when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(
- InstrumentationRegistry.getContext().getSystemService(
- Context.DISPLAY_SERVICE));
-
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
when(mMockBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
+ mTestableContext.addMockService(COMPONENT_NAME, mMockBinder);
mAccessibilityServiceConnection = new AccessibilityServiceConnection(
- mUserState,
- mMockContext,
+ userState,
+ mTestableContext,
COMPONENT_NAME,
mMockServiceInfo,
SERVICE_ID,
@@ -170,74 +209,147 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
mMockA11yWindowManager,
mMockActivityTaskManagerInternal);
mAccessibilityServiceConnection.bindLocked();
- mAccessibilityServiceConnection.onServiceConnected(COMPONENT_NAME, mMockBinder);
}
@SmallTest
+ @Test
public void testRegisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
- fail();
+ Assert.fail();
} catch (SecurityException expected) {
}
verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
}
@SmallTest
+ @Test
public void testRegisterSystemAction() throws Exception {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION);
}
- @SmallTest
+ @Test
public void testUnregisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.unregisterSystemAction(ACTION_ID);
- fail();
+ Assert.fail();
} catch (SecurityException expected) {
}
verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
}
@SmallTest
+ @Test
public void testUnregisterSystemAction() throws Exception {
mA11yms.unregisterSystemAction(ACTION_ID);
verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID);
}
@SmallTest
+ @Test
public void testOnSystemActionsChanged() throws Exception {
setupAccessibilityServiceConnection();
- mA11yms.notifySystemActionsChangedLocked(mUserState);
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.notifySystemActionsChangedLocked(userState);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
verify(mMockServiceClient).onSystemActionsChanged();
}
@SmallTest
+ @Test
public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+ );
+
+ mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, false);
+
+ Assert.assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY));
+ }
+
+ @SmallTest
+ @Test
+ public void testOnMagnificationTransitionSuccess_capabilitiesIsAll_inputFilterRefreshMode() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
userState.setMagnificationCapabilitiesLocked(
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
- userState.setMagnificationModeLocked(
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+ );
+
+ mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, true);
+
+ ArgumentCaptor<Display> displayCaptor = ArgumentCaptor.forClass(Display.class);
+ verify(mInputFilter, timeout(100)).refreshMagnificationMode(displayCaptor.capture());
+ Assert.assertEquals(Display.DEFAULT_DISPLAY, displayCaptor.getValue().getDisplayId());
+ }
+
+ @SmallTest
+ @Test
+ public void testChangeMagnificationModeOnDefaultDisplay_capabilitiesIsAll_persistChangedMode()
+ throws Exception {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+ );
+
+ mA11yms.changeMagnificationMode(Display.DEFAULT_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ TestUtils.waitUntil("magnification mode " + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
+ + " is not persisted in setting", 1,
+ () -> {
+ final int userMode = Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
+ ACCESSIBILITY_MAGNIFICATION_MODE_NONE,
+ mA11yms.getCurrentUserIdLocked());
+ return userMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ });
+ }
- mA11yms.onMagnificationTransitionEndedLocked(false);
+ @SmallTest
+ @Test
+ public void testChangeMagnificationModeOnTestDisplay_capabilitiesIsAll_transitMode() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(TEST_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+ );
+
+ mA11yms.changeMagnificationMode(TEST_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
- userState.getMagnificationModeLocked());
+ verify(mMockMagnificationController).transitionMagnificationModeLocked(eq(TEST_DISPLAY),
+ eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), ArgumentMatchers.isNotNull());
}
@SmallTest
+ @Test
public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
- mUserState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME);
- mUserState.setMagnificationCapabilitiesLocked(
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+ userState.setMagnificationCapabilitiesLocked(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
// Invokes client change to trigger onUserStateChanged.
@@ -247,6 +359,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
}
@SmallTest
+ @Test
public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() {
setupAccessibilityServiceConnection();
when(mMockSecurityPolicy.canControlMagnification(any())).thenReturn(true);
@@ -256,4 +369,11 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
verify(mMockWindowMagnificationMgr).requestConnection(true);
}
+
+ public static class FakeInputFilter extends AccessibilityInputFilter {
+ FakeInputFilter(Context context,
+ AccessibilityManagerService service) {
+ super(context, service);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 81ca92cc3c8c..b9d94edc5981 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -48,6 +48,7 @@ import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.ArraySet;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
@@ -79,6 +80,8 @@ public class AccessibilityUserStateTest {
private static final int USER_ID = 42;
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+
// Mock package-private class AccessibilityServiceConnection
@Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
@@ -143,7 +146,8 @@ public class AccessibilityUserStateTest {
mUserState.setAutoclickEnabledLocked(true);
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
- mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ mUserState.setMagnificationModeLocked(TEST_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
mUserState.setFocusAppearanceLocked(20, Color.BLUE);
mUserState.onSwitchToAnotherUserLocked();
@@ -165,7 +169,7 @@ public class AccessibilityUserStateTest {
assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
- mUserState.getMagnificationModeLocked());
+ mUserState.getMagnificationModeLocked(TEST_DISPLAY));
assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
}
@@ -360,12 +364,13 @@ public class AccessibilityUserStateTest {
@Test
public void setWindowMagnificationMode_returnExpectedMagnificationMode() {
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
- mUserState.getMagnificationModeLocked());
+ mUserState.getMagnificationModeLocked(TEST_DISPLAY));
- mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ mUserState.setMagnificationModeLocked(TEST_DISPLAY,
+ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
- mUserState.getMagnificationModeLocked());
+ mUserState.getMagnificationModeLocked(TEST_DISPLAY));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 69061c14c70e..8a521d8a7490 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -150,7 +150,7 @@ public class MagnificationControllerTest {
MODE_WINDOW,
mTransitionCallBack);
- verify(mTransitionCallBack).onResult(true);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
verify(mScreenMagnificationController, never()).reset(anyInt(),
any(MagnificationAnimationCallback.class));
verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(),
@@ -171,7 +171,7 @@ public class MagnificationControllerTest {
mCallbackArgumentCaptor.capture());
mCallbackArgumentCaptor.getValue().onResult(true);
mMockConnection.invokeCallbacks();
- verify(mTransitionCallBack).onResult(true);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
}
@@ -189,7 +189,7 @@ public class MagnificationControllerTest {
mTransitionCallBack);
mMockConnection.invokeCallbacks();
- verify(mTransitionCallBack).onResult(true);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
}
@@ -225,7 +225,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
- verify(mTransitionCallBack).onResult(true);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
}
@Test
@@ -245,7 +245,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
MAGNIFICATION_GESTURE_HANDLER_ID);
- verify(mTransitionCallBack).onResult(true);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
}
@Test
@@ -265,7 +265,7 @@ public class MagnificationControllerTest {
0);
assertEquals(MAGNIFIED_CENTER_Y, mScreenMagnificationController.getCenterY(TEST_DISPLAY),
0);
- verify(mTransitionCallBack).onResult(true);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, true);
}
@Test
@@ -285,7 +285,7 @@ public class MagnificationControllerTest {
verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, MAGNIFICATION_GESTURE_HANDLER_ID);
- verify(mTransitionCallBack).onResult(false);
+ verify(mTransitionCallBack).onResult(TEST_DISPLAY, false);
}
@Test
@@ -595,6 +595,13 @@ public class MagnificationControllerTest {
verify(mScaleProvider).onUserRemoved(SECOND_USER_ID);
}
+ @Test
+ public void onChangeMagnificationMode_delegateToService() {
+ mMagnificationController.onChangeMagnificationMode(TEST_DISPLAY, MODE_WINDOW);
+
+ verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_WINDOW);
+ }
+
private void setMagnificationEnabled(int mode) throws RemoteException {
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index f4a83c3c5a23..36c37c4dbf2a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -72,6 +72,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.util.IntArray;
import androidx.test.filters.MediumTest;
@@ -109,6 +110,7 @@ import java.util.function.Function;
* Build/Install/Run:
* atest FrameworksServicesTests:ActivityManagerServiceTest
*/
+@Presubmit
@SmallTest
public class ActivityManagerServiceTest {
private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
index 0db118d3db47..8a3f246fd656 100644
--- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -42,6 +42,7 @@ import android.app.IUidObserver;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.SparseArray;
@@ -57,6 +58,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+@Presubmit
@SmallTest
public class UidObserverControllerTest {
private static final int TEST_UID1 = 1111;
diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
index a2ecbc30ec64..c97a67bad590 100644
--- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
@@ -14,19 +14,25 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.health;
-import static junit.framework.Assert.*;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.fail;
import static org.mockito.Mockito.*;
import android.hardware.health.V2_0.IHealth;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
-import android.test.AndroidTestCase;
+import android.os.RemoteException;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -36,36 +42,39 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.NoSuchElementException;
-public class BatteryServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class HealthServiceWrapperTest {
@Mock IServiceManager mMockedManager;
@Mock IHealth mMockedHal;
@Mock IHealth mMockedHal2;
- @Mock BatteryService.HealthServiceWrapper.Callback mCallback;
- @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier;
- @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier;
- BatteryService.HealthServiceWrapper mWrapper;
+ @Mock HealthServiceWrapperHidl.Callback mCallback;
+ @Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier;
+ @Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier;
+ HealthServiceWrapper mWrapper;
- private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR;
+ private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR;
- @Override
+ @Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
- @Override
+ @After
public void tearDown() {
- if (mWrapper != null)
- mWrapper.getHandlerThread().quitSafely();
+ if (mWrapper != null) mWrapper.getHandlerThread().quitSafely();
}
public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
return new ArgumentMatcher<T>() {
- @Override public boolean matches(T e) {
+ @Override
+ public boolean matches(T e) {
return collection.contains(e);
}
- @Override public String toString() {
+
+ @Override
+ public String toString() {
return collection.toString();
}
};
@@ -73,27 +82,29 @@ public class BatteryServiceTest extends AndroidTestCase {
private void initForInstances(String... instanceNamesArr) throws Exception {
final Collection<String> instanceNames = Arrays.asList(instanceNamesArr);
- doAnswer((invocation) -> {
- // technically, preexisting is ignored by
- // BatteryService.HealthServiceWrapper.Notification, but still call it correctly.
- sendNotification(invocation, true);
- sendNotification(invocation, true);
- sendNotification(invocation, false);
- return null;
- }).when(mMockedManager).registerForNotifications(
- eq(IHealth.kInterfaceName),
- argThat(isOneOf(instanceNames)),
- any(IServiceNotification.class));
+ doAnswer(
+ (invocation) -> {
+ // technically, preexisting is ignored by
+ // HealthServiceWrapperHidl.Notification, but still call it correctly.
+ sendNotification(invocation, true);
+ sendNotification(invocation, true);
+ sendNotification(invocation, false);
+ return null;
+ })
+ .when(mMockedManager)
+ .registerForNotifications(
+ eq(IHealth.kInterfaceName),
+ argThat(isOneOf(instanceNames)),
+ any(IServiceNotification.class));
doReturn(mMockedManager).when(mManagerSupplier).get();
- doReturn(mMockedHal) // init calls this
- .doReturn(mMockedHal) // notification 1
- .doReturn(mMockedHal) // notification 2
- .doReturn(mMockedHal2) // notification 3
- .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
- .when(mHealthServiceSupplier).get(argThat(isOneOf(instanceNames)));
-
- mWrapper = new BatteryService.HealthServiceWrapper();
+ doReturn(mMockedHal) // init calls this
+ .doReturn(mMockedHal) // notification 1
+ .doReturn(mMockedHal) // notification 2
+ .doReturn(mMockedHal2) // notification 3
+ .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
+ .when(mHealthServiceSupplier)
+ .get(argThat(isOneOf(instanceNames)));
}
private void waitHandlerThreadFinish() throws Exception {
@@ -108,16 +119,20 @@ public class BatteryServiceTest extends AndroidTestCase {
private static void sendNotification(InvocationOnMock invocation, boolean preexisting)
throws Exception {
- ((IServiceNotification)invocation.getArguments()[2]).onRegistration(
- IHealth.kInterfaceName,
- (String)invocation.getArguments()[1],
- preexisting);
+ ((IServiceNotification) invocation.getArguments()[2])
+ .onRegistration(
+ IHealth.kInterfaceName, (String) invocation.getArguments()[1], preexisting);
+ }
+
+ private void createWrapper() throws RemoteException {
+ mWrapper = HealthServiceWrapper.create(mCallback, mManagerSupplier, mHealthServiceSupplier);
}
@SmallTest
+ @Test
public void testWrapPreferVendor() throws Exception {
initForInstances(VENDOR);
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
+ createWrapper();
waitHandlerThreadFinish();
verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
@@ -125,10 +140,11 @@ public class BatteryServiceTest extends AndroidTestCase {
}
@SmallTest
+ @Test
public void testNoService() throws Exception {
initForInstances("unrelated");
try {
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
+ createWrapper();
fail("Expect NoSuchElementException");
} catch (NoSuchElementException ex) {
// expected
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 4f77afb4969f..f45c869949b5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -46,18 +46,6 @@ import android.app.IUidObserver;
import android.app.PendingIntent;
import android.app.Person;
import android.app.admin.DevicePolicyManager;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.PackageIdentifier;
-import android.app.appsearch.SearchResultPage;
-import android.app.appsearch.SetSchemaResponse;
-import android.app.appsearch.aidl.AppSearchBatchResultParcel;
-import android.app.appsearch.aidl.AppSearchResultParcel;
-import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
-import android.app.appsearch.aidl.IAppSearchManager;
-import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
@@ -92,9 +80,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
-import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -106,6 +92,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.infra.AndroidFuture;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
@@ -168,7 +155,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
case Context.DEVICE_POLICY_SERVICE:
return mMockDevicePolicyManager;
case Context.APP_SEARCH_SERVICE:
- return new AppSearchManager(this, mMockAppSearchManager);
case Context.ROLE_SERVICE:
// RoleManager is final and cannot be mocked, so we only override the inject
// accessor methods in ShortcutService.
@@ -647,260 +633,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
}
}
- protected class MockAppSearchManager implements IAppSearchManager {
-
- protected Map<String, List<PackageIdentifier>> mSchemasVisibleToPackages =
- new ArrayMap<>(1);
- private Map<String, Map<String, GenericDocument>> mDocumentMap = new ArrayMap<>(1);
-
- private String getKey(UserHandle userHandle, String databaseName) {
- return userHandle.getIdentifier() + "@" + databaseName;
- }
-
- @Override
- public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
- List<String> schemasNotDisplayedBySystem,
- Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride,
- int version, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback) throws RemoteException {
- for (Map.Entry<String, List<Bundle>> entry :
- schemasVisibleToPackagesBundles.entrySet()) {
- final String key = entry.getKey();
- final List<PackageIdentifier> packageIdentifiers;
- if (!mSchemasVisibleToPackages.containsKey(key)) {
- packageIdentifiers = new ArrayList<>(entry.getValue().size());
- mSchemasVisibleToPackages.put(key, packageIdentifiers);
- } else {
- packageIdentifiers = mSchemasVisibleToPackages.get(key);
- }
- for (int i = 0; i < entry.getValue().size(); i++) {
- packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
- }
- }
- final SetSchemaResponse response = new SetSchemaResponse.Builder().build();
- callback.onResult(
- new AppSearchResultParcel(
- AppSearchResult.newSuccessfulResult(response.getBundle())));
- }
-
- @Override
- public void getSchema(String packageName, String databaseName, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void getNamespaces(String packageName, String databaseName, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void putDocuments(String packageName, String databaseName,
- List<Bundle> documentBundles, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchBatchResultCallback callback)
- throws RemoteException {
- final List<GenericDocument> docs = new ArrayList<>(documentBundles.size());
- for (Bundle bundle : documentBundles) {
- docs.add(new GenericDocument(bundle));
- }
- final AppSearchBatchResult.Builder<String, Void> builder =
- new AppSearchBatchResult.Builder<>();
- final String key = getKey(userHandle, databaseName);
- Map<String, GenericDocument> docMap = mDocumentMap.get(key);
- for (GenericDocument doc : docs) {
- builder.setSuccess(doc.getId(), null);
- if (docMap == null) {
- docMap = new ArrayMap<>(1);
- mDocumentMap.put(key, docMap);
- }
- docMap.put(doc.getId(), doc);
- }
- callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
- }
-
- @Override
- public void getDocuments(String packageName, String databaseName, String namespace,
- List<String> ids, Map<String, List<String>> typePropertyPaths,
- UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchBatchResultCallback callback) throws RemoteException {
- final AppSearchBatchResult.Builder<String, Bundle> builder =
- new AppSearchBatchResult.Builder<>();
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- for (String id : ids) {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- key + " not found when getting: " + id);
- }
- } else {
- final Map<String, GenericDocument> docs = mDocumentMap.get(key);
- for (String id : ids) {
- if (docs.containsKey(id)) {
- builder.setSuccess(id, docs.get(id).getBundle());
- } else {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- "shortcut not found: " + id);
- }
- }
- }
- callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
- }
-
- @Override
- public void query(String packageName, String databaseName, String queryExpression,
- Bundle searchSpecBundle, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback)
- throws RemoteException {
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- final Bundle page = new Bundle();
- page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
- page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
- return;
- }
- final List<GenericDocument> documents = new ArrayList<>(mDocumentMap.get(key).values());
- final Bundle page = new Bundle();
- page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 0);
- final ArrayList<Bundle> resultBundles = new ArrayList<>();
- for (GenericDocument document : documents) {
- final Bundle resultBundle = new Bundle();
- resultBundle.putBundle("document", document.getBundle());
- resultBundle.putString("packageName", packageName);
- resultBundle.putString("databaseName", databaseName);
- resultBundle.putParcelableArrayList("matches", new ArrayList<>());
- resultBundles.add(resultBundle);
- }
- page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
- }
-
- @Override
- public void globalQuery(String packageName, String queryExpression, Bundle searchSpecBundle,
- UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void getNextPage(String packageName, long nextPageToken, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- final Bundle page = new Bundle();
- page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
- page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
- }
-
- @Override
- public void invalidateNextPageToken(String packageName, long nextPageToken,
- UserHandle userHandle) throws RemoteException {
- }
-
- @Override
- public void writeQueryResultsToFile(String packageName, String databaseName,
- ParcelFileDescriptor fileDescriptor, String queryExpression,
- Bundle searchSpecBundle, UserHandle userHandle, IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void putDocumentsFromFile(String packageName, String databaseName,
- ParcelFileDescriptor fileDescriptor, UserHandle userHandle,
- IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void reportUsage(String packageName, String databaseName, String namespace,
- String documentId, long usageTimestampMillis, boolean systemUsage,
- UserHandle userHandle,
- IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void removeByDocumentId(String packageName, String databaseName, String namespace,
- List<String> ids, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchBatchResultCallback callback)
- throws RemoteException {
- final AppSearchBatchResult.Builder<String, Void> builder =
- new AppSearchBatchResult.Builder<>();
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- for (String id : ids) {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- "package " + key + " not found when removing " + id);
- }
- } else {
- final Map<String, GenericDocument> docs = mDocumentMap.get(key);
- for (String id : ids) {
- if (docs.containsKey(id)) {
- docs.remove(id);
- builder.setSuccess(id, null);
- } else {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- "shortcut not found when removing " + id);
- }
- }
- }
- callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
- }
-
- @Override
- public void removeByQuery(String packageName, String databaseName, String queryExpression,
- Bundle searchSpecBundle, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback)
- throws RemoteException {
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
- return;
- }
- mDocumentMap.get(key).clear();
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
- }
-
- @Override
- public void getStorageInfo(String packageName, String databaseName, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void persistToDisk(String packageName, UserHandle userHandle,
- long binderCallStartTimeMillis) throws RemoteException {
- }
-
- @Override
- public void initialize(String packageName, UserHandle userHandle,
- long binderCallStartTimeMillis, IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- private void removeShortcuts() {
- mDocumentMap.clear();
- }
-
- private void ignore(IAppSearchResultCallback callback) throws RemoteException {
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
- }
- }
-
public static class ShortcutActivity extends Activity {
}
@@ -952,7 +684,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected PackageManagerInternal mMockPackageManagerInternal;
protected UserManager mMockUserManager;
protected DevicePolicyManager mMockDevicePolicyManager;
- protected MockAppSearchManager mMockAppSearchManager;
protected UserManagerInternal mMockUserManagerInternal;
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
protected ActivityManagerInternal mMockActivityManagerInternal;
@@ -1102,7 +833,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockUserManager = mock(UserManager.class);
mMockDevicePolicyManager = mock(DevicePolicyManager.class);
- mMockAppSearchManager = new MockAppSearchManager();
mMockUserManagerInternal = mock(UserManagerInternal.class);
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
@@ -1314,9 +1044,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
shutdownServices();
- mMockAppSearchManager.removeShortcuts();
- mMockAppSearchManager = null;
-
super.tearDown();
}
@@ -2235,6 +1962,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
return p == null ? null : p.getAllShareTargetsForTest();
}
+ protected void resetPersistedShortcuts() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ p.removeAllShortcutsAsync();
+ }
+
+ protected void getPersistedShortcut(AndroidFuture<List<ShortcutInfo>> cb) {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ p.getTopShortcutsFromPersistence(cb);
+ }
+
/**
* @return the number of shortcuts stored internally for the caller that can be used as a share
* target in the ShareSheet. Such shortcuts have a matching category with at least one of the
@@ -2425,8 +2164,6 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
deleteAllSavedFiles();
- mMockAppSearchManager.removeShortcuts();
-
initService();
mService.applyRestore(payload, USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 9598a00df33d..bc2d2563b12d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -18,9 +18,20 @@ package com.android.server.pm;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
import android.app.PendingIntent;
+import android.content.pm.ShortcutInfo;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
/**
* Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
*
@@ -28,6 +39,21 @@ import android.os.UserHandle;
*/
public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mService.updateConfigurationLocked(
+ ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
+ + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0).removeAllShortcutsAsync();
+ super.tearDown();
+ }
+
public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
setDefaultLauncher(USER_0, LAUNCHER_1);
@@ -41,4 +67,201 @@ public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
assertNotNull(intent);
});
}
+
+ public void testSetDynamicShortcuts_PersistsShortcutsToDisk() throws RemoteException {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ // Verifies setDynamicShortcuts persists shortcuts into AppSearch
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3")
+ ));
+ List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(3, shortcuts.size());
+ Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+
+ // Verifies removeAllDynamicShortcuts removes shortcuts from persistence layer
+ mManager.removeAllDynamicShortcuts();
+ shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertTrue(shortcuts.isEmpty());
+ }
+
+ public void testAddDynamicShortcuts_PersistsShortcutsToDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3")
+ ));
+ // Verifies addDynamicShortcuts persists shortcuts into AppSearch
+ mManager.addDynamicShortcuts(list(makeShortcut("s4"), makeShortcut("s5")));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testPushDynamicShortcuts_PersistsShortcutsToDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ // Verifies pushDynamicShortcuts further persists shortcuts into AppSearch without
+ // removing previous shortcuts when max number of shortcuts is reached.
+ mManager.pushDynamicShortcut(makeShortcut("s6"));
+ shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(6, shortcuts.size());
+ shortcutIds = shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ assertTrue(shortcutIds.contains("s6"));
+ }
+
+ public void testRemoveDynamicShortcuts_RemovesShortcutsFromDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+
+ // Verifies removeDynamicShortcuts removes shortcuts from persistence layer
+ mManager.removeDynamicShortcuts(list("s1"));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(4, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testRemoveLongLivedShortcuts_RemovesShortcutsFromDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ mManager.removeDynamicShortcuts(list("s2"));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(4, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testDisableShortcuts_RemovesShortcutsFromDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ // Verifies disableShortcuts removes shortcuts from persistence layer
+ mManager.disableShortcuts(list("s3"));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(4, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testUpdateShortcuts_UpdateShortcutsOnDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ // Verifies disableShortcuts removes shortcuts from persistence layer
+ mManager.updateShortcuts(list(makeShortcutWithShortLabel("s3", "custom")));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ final Map<String, ShortcutInfo> map = shortcuts.stream()
+ .collect(Collectors.toMap(ShortcutInfo::getId, Function.identity()));
+ assertTrue(map.containsKey("s3"));
+ assertEquals("custom", map.get("s3").getShortLabel());
+ }
+
+ private List<ShortcutInfo> getAllPersistedShortcuts() {
+ try {
+ SystemClock.sleep(500);
+ final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
+ getPersistedShortcut(future);
+ return future.get(10, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
new file mode 100644
index 000000000000..ddb9b43be76f
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@RunWithLooper
+public class CountdownConditionProviderTest extends UiServiceTestCase {
+
+ CountdownConditionProvider mService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Intent startIntent =
+ new Intent("com.android.server.notification.CountdownConditionProvider");
+ startIntent.setPackage("android");
+ CountdownConditionProvider service = new CountdownConditionProvider();
+ service.attach(
+ getContext(),
+ null, // ActivityThread not actually used in Service
+ CountdownConditionProvider.class.getName(),
+ null, // token not needed when not talking with the activity manager
+ mock(Application.class),
+ null // mocked services don't talk with the activity manager
+ );
+ service.onCreate();
+ service.onBind(startIntent);
+ mService = spy(service);
+ }
+
+ @Test
+ public void testGetPendingIntent() {
+ PendingIntent pi = mService.getPendingIntent(Uri.EMPTY);
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
new file mode 100644
index 000000000000..4c440cabf9c4
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@RunWithLooper
+public class EventConditionProviderTest extends UiServiceTestCase {
+
+ EventConditionProvider mService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Intent startIntent =
+ new Intent("com.android.server.notification.EventConditionProvider");
+ startIntent.setPackage("android");
+ EventConditionProvider service = new EventConditionProvider();
+ service.attach(
+ getContext(),
+ null, // ActivityThread not actually used in Service
+ CountdownConditionProvider.class.getName(),
+ null, // token not needed when not talking with the activity manager
+ mock(Application.class),
+ null // mocked services don't talk with the activity manager
+ );
+ service.onCreate();
+ service.onBind(startIntent);
+ mService = spy(service);
+ }
+
+ @Test
+ public void testGetPendingIntent() {
+ PendingIntent pi = mService.getPendingIntent(1000);
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
+ }
+}
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 def38a6427a7..5a5a4e754390 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -142,6 +142,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioManager;
+import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -214,6 +215,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -478,6 +480,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
+ Mockito.doReturn(new Intent()).when(mContext).registerReceiverAsUser(
+ any(), any(), any(), any(), any());
+ Mockito.doReturn(new Intent()).when(mContext).registerReceiver(any(), any());
verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
any(), intentFilterCaptor.capture(), any(), any());
verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
@@ -8461,4 +8466,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
fail("call to matchesCallFilter with listener permissions should work");
}
}
+
+ @Test
+ public void testMediaNotificationsBypassBlock() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.setNotificationsEnabledForPackage(
+ r.getSbn().getPackageName(), r.getUid(), false);
+
+ // normal blocked notifications - blocked
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // just using the style - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // style + media session - bypasses block
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 423ba94729ff..011d190ea83a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -23,6 +23,8 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;
@@ -84,6 +86,7 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.res.Resources;
import android.media.AudioManager;
+import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -711,4 +714,102 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
assertThat(r.isImportanceFixed()).isTrue();
}
+
+ @Test
+ public void testMediaNotificationsBypassBlock() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ // normal blocked notifications - blocked
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // just using the style - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // style + media session - bypasses block
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ }
+
+ @Test
+ public void testMediaNotificationsBypassBlock_atPost() throws Exception {
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
+
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats).registerBlocked(any());
+ verify(mUsageStats, never()).registerPostedByApp(any());
+
+ // just using the style - blocked
+ mService.clearNotifications();
+ reset(mUsageStats);
+ nb.setStyle(new Notification.MediaStyle());
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+ runnable = mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats).registerBlocked(any());
+ verify(mUsageStats, never()).registerPostedByApp(any());
+
+ // style + media session - bypasses block
+ mService.clearNotifications();
+ reset(mUsageStats);
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+ runnable = mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats, never()).registerBlocked(any());
+ verify(mUsageStats).registerPostedByApp(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d4991fce8047..2954d783c149 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1084,6 +1084,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1115,6 +1116,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1147,6 +1149,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
setUpDisplaySizeWithApp(1000, 1200);
@@ -1175,6 +1178,52 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
+ // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+ // isn't applied.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().height(), 0.5);
+ assertEquals(1000, activity.getBounds().width());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioWithoutGlobalOverride() {
// In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index cbdf4fe4551f..d89d64ad0037 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -254,7 +254,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
}
@@ -276,7 +277,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
}
@@ -301,7 +303,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -337,8 +340,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
- taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+ taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -391,7 +396,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 42f4d583f5ff..6737b1ade785 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -20,12 +20,15 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Rect;
+import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.MediumTest;
@@ -64,6 +67,7 @@ public class TaskFragmentTest extends WindowTestsBase {
mTaskFragment = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
.setOrganizer(mOrganizer)
+ .setFragmentToken(new Binder())
.build();
mLeash = mTaskFragment.getSurfaceControl();
spyOn(mTaskFragment);
@@ -103,4 +107,23 @@ public class TaskFragmentTest extends WindowTestsBase {
verify(mTransaction).setPosition(mLeash, 500, 500);
verify(mTransaction).setWindowCrop(mLeash, 500, 500);
}
+
+ /**
+ * Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an
+ * activity that has not yet been attached to a process because it is being initialized but
+ * belongs to the TaskFragmentOrganizer process is still reported in the TaskFragmentInfo.
+ */
+ @Test
+ public void testActivityStillReported_NotYetAssignedToProcess() {
+ mTaskFragment.addChild(new ActivityBuilder(mAtm).setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID)
+ .setProcessName(DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME).build());
+ final ActivityRecord activity = mTaskFragment.getTopMostActivity();
+ // Remove the process to simulate an activity that has not yet been attached to a process
+ activity.app = null;
+ final TaskFragmentInfo info = activity.getTaskFragment().getTaskFragmentInfo();
+ assertEquals(1, info.getRunningActivityCount());
+ assertEquals(1, info.getActivities().size());
+ assertEquals(false, info.isEmpty());
+ assertEquals(activity.token, info.getActivities().get(0));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 386ff4b80933..996b4b22dde7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -126,6 +126,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
+ static final int DEFAULT_TASK_FRAGMENT_ORGANIZER_UID = 10000;
+ static final String DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME = "Test:TaskFragmentOrganizer";
+
// Default base activity name
private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity";
@@ -1234,7 +1237,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
if (mOrganizer != null) {
taskFragment.setTaskFragmentOrganizer(
- mOrganizer.getOrganizerToken(), 10000 /* pid */);
+ mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID,
+ DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
}
return taskFragment;
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 0bd7b20c1364..2d3b9286d69a 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -31,8 +31,11 @@ import android.app.usage.ExternalStorageStats;
import android.app.usage.IStorageStatsManager;
import android.app.usage.StorageStats;
import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -155,6 +158,21 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
});
LocalManagerRegistry.addManager(StorageStatsManagerLocal.class, new LocalService());
+
+ IntentFilter prFilter = new IntentFilter();
+ prFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ prFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ prFilter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ mHandler.removeMessages(H.MSG_PACKAGE_REMOVED);
+ mHandler.sendEmptyMessage(H.MSG_PACKAGE_REMOVED);
+ }
+ }
+ }, prFilter);
}
private void invalidateMounts() {
@@ -531,6 +549,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
private static final int MSG_CHECK_STORAGE_DELTA = 100;
private static final int MSG_LOAD_CACHED_QUOTAS_FROM_FILE = 101;
private static final int MSG_RECALCULATE_QUOTAS = 102;
+ private static final int MSG_PACKAGE_REMOVED = 103;
/**
* By only triggering a re-calculation after the storage has changed sizes, we can avoid
* recalculating quotas too often. Minimum change delta defines the percentage of change
@@ -599,6 +618,11 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
sendEmptyMessageDelayed(MSG_RECALCULATE_QUOTAS, DELAY_RECALCULATE_QUOTAS);
break;
}
+ case MSG_PACKAGE_REMOVED: {
+ // recalculate quotas when package is removed
+ recalculateQuotas(getInitializedStrategy());
+ break;
+ }
default:
if (DEBUG) {
Slog.v(TAG, ">>> default message case ");
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 6dbf4c56da3b..a32bd2dadc18 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -403,6 +403,7 @@ public class UsageStatsService extends SystemService implements
if (userId == UserHandle.USER_SYSTEM) {
UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
}
+ final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId));
synchronized (mLock) {
// This should be safe to add this early. Other than reportEventOrAddToQueue and
// getBackupPayload, every other user grabs the lock before accessing
@@ -425,7 +426,7 @@ public class UsageStatsService extends SystemService implements
boolean needToFlush = !pendingEvents.isEmpty();
initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(),
- installedPackages);
+ installedPackages, deleteObsoleteData);
final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId);
if (userService == null) {
Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
@@ -651,13 +652,13 @@ public class UsageStatsService extends SystemService implements
* when the user is initially unlocked.
*/
private void initializeUserUsageStatsServiceLocked(int userId, long currentTimeMillis,
- HashMap<String, Long> installedPackages) {
+ HashMap<String, Long> installedPackages, boolean deleteObsoleteData) {
final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
"usagestats");
final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId,
usageStatsDir, this);
try {
- service.init(currentTimeMillis, installedPackages);
+ service.init(currentTimeMillis, installedPackages, deleteObsoleteData);
mUserState.put(userId, service);
} catch (Exception e) {
if (mUserManager.isUserUnlocked(userId)) {
@@ -1246,6 +1247,10 @@ public class UsageStatsService extends SystemService implements
* Called by the Binder stub.
*/
private boolean updatePackageMappingsData() {
+ // don't update the mappings if a profile user is defined
+ if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) {
+ return true; // return true so job scheduler doesn't reschedule the job
+ }
// fetch the installed packages outside the lock so it doesn't block package manager.
final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
synchronized (mLock) {
@@ -1584,6 +1589,13 @@ public class UsageStatsService extends SystemService implements
mEstimatedLaunchTimeChangedListeners.remove(listener);
}
+ private boolean shouldDeleteObsoleteData(UserHandle userHandle) {
+ final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+ // If a profile owner is not defined for the given user, obsolete data should be deleted
+ return dpmInternal == null
+ || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null;
+ }
+
private String buildFullToken(String packageName, String token) {
final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
sb.append(packageName);
@@ -2862,8 +2874,12 @@ public class UsageStatsService extends SystemService implements
private class MyPackageMonitor extends PackageMonitor {
@Override
public void onPackageRemoved(String packageName, int uid) {
- mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName)
- .sendToTarget();
+ final int changingUserId = getChangingUserId();
+ // Only remove the package's data if a profile owner is not defined for the user
+ if (shouldDeleteObsoleteData(UserHandle.of(changingUserId))) {
+ mHandler.obtainMessage(MSG_PACKAGE_REMOVED, changingUserId, 0, packageName)
+ .sendToTarget();
+ }
super.onPackageRemoved(packageName, uid);
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 23694fc5b418..79f5808739db 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -117,8 +117,9 @@ class UserUsageStatsService {
mSystemTimeSnapshot = System.currentTimeMillis();
}
- void init(final long currentTimeMillis, HashMap<String, Long> installedPackages) {
- readPackageMappingsLocked(installedPackages);
+ void init(final long currentTimeMillis, HashMap<String, Long> installedPackages,
+ boolean deleteObsoleteData) {
+ readPackageMappingsLocked(installedPackages, deleteObsoleteData);
mDatabase.init(currentTimeMillis);
if (mDatabase.wasUpgradePerformed()) {
mDatabase.prunePackagesDataOnUpgrade(installedPackages);
@@ -182,12 +183,13 @@ class UserUsageStatsService {
return mDatabase.onPackageRemoved(packageName, timeRemoved);
}
- private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
+ private void readPackageMappingsLocked(HashMap<String, Long> installedPackages,
+ boolean deleteObsoleteData) {
mDatabase.readMappingsLocked();
// Package mappings for the system user are updated after 24 hours via a job scheduled by
// UsageStatsIdleService to ensure restored data is not lost on first boot. Additionally,
// this makes user service initialization a little quicker on subsequent boots.
- if (mUserId != UserHandle.USER_SYSTEM) {
+ if (mUserId != UserHandle.USER_SYSTEM && deleteObsoleteData) {
updatePackageMappingsLocked(installedPackages);
}
}
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index ae597e02f33c..2b355ae216e3 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -16,11 +16,14 @@
package android.telephony;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.RadioAccessSpecifier;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -32,7 +35,6 @@ import java.util.Objects;
* Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
*/
public final class AvailableNetworkInfo implements Parcelable {
-
/*
* Defines number of priority level high.
*/
@@ -48,6 +50,14 @@ public final class AvailableNetworkInfo implements Parcelable {
*/
public static final int PRIORITY_LOW = 3;
+ /** @hide */
+ @IntDef({
+ PRIORITY_HIGH,
+ PRIORITY_MED,
+ PRIORITY_LOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AvailableNetworkInfoPriority {}
/**
* subscription Id of the available network. This value must be one of the entry retrieved from
* {@link SubscriptionManager#getOpportunisticSubscriptions}
@@ -62,7 +72,7 @@ public final class AvailableNetworkInfo implements Parcelable {
* for network selection. If there are more than one subId with highest priority then the
* network with highest RSRP is chosen.
*/
- private int mPriority;
+ private @AvailableNetworkInfoPriority int mPriority;
/**
* Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
@@ -77,8 +87,7 @@ public final class AvailableNetworkInfo implements Parcelable {
* Opportunistic network service will use these bands to scan.
*
* When no specific bands are specified (empty array or null) CBRS band
- * {@link AccessNetworkConstants.EutranBand.BAND_48
- * } will be used for network scan.
+ * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
*
* See {@link AccessNetworkConstants} for details.
*
@@ -94,7 +103,7 @@ public final class AvailableNetworkInfo implements Parcelable {
* If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
* of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
* AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
- * by Opportunistic network service.
+ * by Opportunistic network service for a network scan.
*/
private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
@@ -117,6 +126,7 @@ public final class AvailableNetworkInfo implements Parcelable {
* network with highest RSRP is chosen.
* @return priority level
*/
+ @AvailableNetworkInfoPriority
public int getPriority() {
return mPriority;
}
@@ -149,15 +159,9 @@ public final class AvailableNetworkInfo implements Parcelable {
* Returns a list of {@link RadioAccessSpecifier} associated with the available network.
* Opportunistic network service will use this to determine which bands to scan for.
*
- * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no
- * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link
- * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
- * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
- * by Opportunistic network service.
* @return the access network type associated with the available network.
- * @hide
*/
- public List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
+ public @NonNull List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
}
@@ -193,9 +197,9 @@ public final class AvailableNetworkInfo implements Parcelable {
}
/** @hide */
- private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
- @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier>
- radioAccessSpecifiers) {
+ private AvailableNetworkInfo(int subId, @AvailableNetworkInfoPriority int priority,
+ @NonNull List<String> mccMncs, @NonNull List<Integer> bands,
+ @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
mSubId = subId;
mPriority = priority;
mMccMncs = new ArrayList<String>(mccMncs);
@@ -261,27 +265,39 @@ public final class AvailableNetworkInfo implements Parcelable {
*
* <pre><code>
*
- * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder()
- * .setSubId(1)
+ * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder(subId)
* .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+ * .setRadioAccessSpecifiers(radioAccessSpecifiers)
+ * .setMccMncs(mccMncs)
* .build();
* </code></pre>
- *
- * @hide
*/
public static final class Builder {
private int mSubId = Integer.MIN_VALUE;
- private int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+ private @AvailableNetworkInfoPriority int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
private ArrayList<String> mMccMncs = new ArrayList<>();
- private ArrayList<Integer> mBands = new ArrayList<>();
private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
- public @NonNull Builder setSubId(int subId) {
+ /**
+ *
+ */
+ /**
+ * Creates an AvailableNetworkInfo Builder with specified subscription id.
+ *
+ * @param subId of the availableNetwork.
+ */
+ public Builder(int subId) {
mSubId = subId;
- return this;
}
- public @NonNull Builder setPriority(int priority) {
+ /**
+ * Sets the priority for the subscription id.
+ *
+ * @param priority of the subscription id. See {@link AvailableNetworkInfo#getPriority} for
+ * more details
+ * @return the original Builder object.
+ */
+ public @NonNull Builder setPriority(@AvailableNetworkInfoPriority int priority) {
if (priority > AvailableNetworkInfo.PRIORITY_LOW
|| priority < AvailableNetworkInfo.PRIORITY_HIGH) {
throw new IllegalArgumentException("A valid priority must be set");
@@ -290,30 +306,48 @@ public final class AvailableNetworkInfo implements Parcelable {
return this;
}
- public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) {
- Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty "
- + "list is still accepted. Please read documentation in "
- + "AvailableNetworkService to see consequences of an empty Arraylist.");
- mMccMncs = mccMncs;
+ /**
+ * Sets the list of mccmncs associated with the subscription id.
+ *
+ * @param mccMncs nonull list of mccmncs. An empty List is still accepted. Please read
+ * documentation in {@link AvailableNetworkInfo} to see consequences of an empty List.
+ * @return the original Builder object.
+ */
+ public @NonNull Builder setMccMncs(@NonNull List<String> mccMncs) {
+ Objects.requireNonNull(mccMncs, "A non-null List of mccmncs must be set. An empty "
+ + "List is still accepted. Please read documentation in "
+ + "AvailableNetworkInfo to see consequences of an empty List.");
+ mMccMncs = new ArrayList<>(mccMncs);
return this;
}
+ /**
+ * Sets the list of mccmncs associated with the subscription id.
+ *
+ * @param radioAccessSpecifiers nonull list of radioAccessSpecifiers. An empty List is still
+ * accepted. Please read documentation in {@link AvailableNetworkInfo} to see
+ * consequences of an empty List.
+ * @return the original Builder object.
+ */
public @NonNull Builder setRadioAccessSpecifiers(
- @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) {
- Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of "
- + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please "
- + "read documentation in AvailableNetworkService to see consequences of an "
- + "empty Arraylist.");
- mRadioAccessSpecifiers = radioAccessSpecifiers;
+ @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
+ Objects.requireNonNull(radioAccessSpecifiers, "A non-null List of "
+ + "RadioAccessSpecifiers must be set. An empty List is still accepted. Please "
+ + "read documentation in AvailableNetworkInfo to see consequences of an "
+ + "empty List.");
+ mRadioAccessSpecifiers = new ArrayList<>(radioAccessSpecifiers);
return this;
}
+ /**
+ * @return an AvailableNetworkInfo object with all the fields previously set by the Builder.
+ */
public @NonNull AvailableNetworkInfo build() {
if (mSubId == Integer.MIN_VALUE) {
throw new IllegalArgumentException("A valid subId must be set");
}
- return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands,
+ return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, new ArrayList<>(),
mRadioAccessSpecifiers);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2141e4288848..2dfa9a452aba 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -3696,6 +3697,49 @@ public class CarrierConfigManager {
"show_wifi_calling_icon_in_status_bar_bool";
/**
+ * Configuration to indicate that the carrier supports opportunistic data
+ * auto provisioning. Based on this flag, the device downloads and activates
+ * corresponding opportunistic profile.
+ */
+ public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL =
+ "carrier_supports_opp_data_auto_provisioning_bool";
+
+ /**
+ * SMDP+ server address for downloading opportunistic eSIM profile.
+ * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
+ * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'.
+ */
+ public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
+ "smdp_server_address_string";
+
+ /**
+ * This timer value is used in the eSIM Exponential Backoff download retry algorithm.
+ * Value should be in seconds.
+ * <OL>
+ * <LI>When the first download failure occurs, retry download after BACKOFF_TIMER_VALUE
+ * seconds.</LI>
+ *
+ * <LI>If download fails again then, retry after either BACKOFF_TIMER_VALUE,
+ * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.</LI>
+ *
+ * <LI>In general after the cth failed attempt, retry after k * BACKOFF_TIMER_VALUE
+ * seconds, where k is a random integer between 1 and 2^c − 1. Max c value is
+ * {@link #KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT}</LI>
+ * </OL>
+ */
+ public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT =
+ "esim_download_retry_backoff_timer_sec_int";
+
+ /**
+ * If eSIM profile download fails then, the number of retry attempts by UE
+ * will be based on this configuration. If download still fails even after the
+ * MAX attempts configured by this item then the retry is postponed until next
+ * device bootup.
+ */
+ public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT =
+ "esim_max_download_retry_attempts_int";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -3905,6 +3949,30 @@ public class CarrierConfigManager {
public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
"enabled_4g_opportunistic_network_scan_bool";
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+ * goes out of service before switching the 5G capability back to primary stack. The idea of
+ * waiting a few seconds is to minimize the calling of the expensive capability switching
+ * operation in the case where CBRS goes back into service shortly after going out of it.
+ *
+ * @hide
+ */
+ public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+ "time_to_switch_back_to_primary_if_opportunistic_oos_long";
+
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+ * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+ * 'ping-ponging' effect where device is constantly witching capability back and forth between
+ * primary and opportunistic stack.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG
+ = "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+
/**
* Indicates zero or more emergency number prefix(es), because some carrier requires
* if users dial an emergency number address with a specific prefix, the combination of the
@@ -5780,6 +5848,10 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false);
sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL, false);
+ sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
+ sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
+ sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
@@ -5823,6 +5895,10 @@ public class CarrierConfigManager {
/* Default value is 2 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
+ sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
+ sDefaults.putInt(
+ KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+ 120000);
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
@@ -5978,12 +6054,15 @@ public class CarrierConfigManager {
* any carrier specific configuration has been applied.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
* @return A {@link PersistableBundle} containing the config for the given subId, or default
* values for an invalid subId.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Nullable
public PersistableBundle getConfigForSubId(int subId) {
try {
@@ -6072,10 +6151,13 @@ public class CarrierConfigManager {
* called to confirm whether any carrier specific configuration has been applied.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @see #getConfigForSubId
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Nullable
public PersistableBundle getConfig() {
return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
@@ -6084,8 +6166,8 @@ public class CarrierConfigManager {
/**
* Determines whether a configuration {@link PersistableBundle} obtained from
* {@link #getConfig()} or {@link #getConfigForSubId(int)} corresponds to an identified carrier.
- * <p>
- * When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}
+ *
+ * <p>When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}
* broadcast which informs it that the carrier configuration has changed, it is possible
* that another reload of the carrier configuration has begun since the intent was sent.
* In this case, the carrier configuration the app fetches (e.g. via {@link #getConfig()})
@@ -6094,14 +6176,12 @@ public class CarrierConfigManager {
* return true because it may belong to another previous identified carrier. Users should
* always call {@link #getConfig()} or {@link #getConfigForSubId(int)} after receiving the
* broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED}.
- * </p>
- * <p>
- * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
+ *
+ * <p>After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
* use this method to confirm whether any carrier specific configuration has been applied.
* Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
* still needs to get the current configuration, it must use this method to verify whether the
* configuration is default or carrier overridden.
- * </p>
*
* @param bundle the configuration bundle to be checked.
* @return boolean true if any carrier specific configuration bundle has been applied, false
@@ -6113,19 +6193,20 @@ public class CarrierConfigManager {
/**
* Calling this method triggers telephony services to fetch the current carrier configuration.
- * <p>
- * Normally this does not need to be called because the platform reloads config on its own.
+ *
+ * <p>Normally this does not need to be called because the platform reloads config on its own.
* This should be called by a carrier service app if it wants to update config at an arbitrary
* moment.
- * </p>
- * <p>Requires that the calling app has carrier privileges.
- * <p>
- * This method returns before the reload has completed, and
- * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
- * arbitrary thread.
- * </p>
- * @see TelephonyManager#hasCarrierPrivileges
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
+ *
+ * <p>This method returns before the reload has completed, and {@link
+ * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void notifyConfigChangedForSubId(int subId) {
try {
ICarrierConfigLoader loader = getICarrierConfigLoader();
@@ -6141,11 +6222,10 @@ public class CarrierConfigManager {
}
/**
- * Request the carrier config loader to update the cofig for phoneId.
- * <p>
- * Depending on simState, the config may be cleared or loaded from config app. This is only used
- * by SubscriptionInfoUpdater.
- * </p>
+ * Request the carrier config loader to update the config for phoneId.
+ *
+ * <p>Depending on simState, the config may be cleared or loaded from config app. This is only
+ * used by SubscriptionInfoUpdater.
*
* @hide
*/
@@ -6216,13 +6296,16 @@ public class CarrierConfigManager {
* Gets the configuration values for a component using its prefix.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @param prefix prefix of the component.
* @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
*
* @see #getConfigForSubId
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Nullable
public PersistableBundle getConfigByComponentForSubId(@NonNull String prefix, int subId) {
PersistableBundle configs = getConfigForSubId(subId);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 02363069891d..50f2abd3c1ca 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13626,15 +13626,18 @@ public class TelephonyManager {
}
/**
- * It indicates whether modem is enabled or not per slot.
- * It's the corresponding status of TelephonyManager.enableModemForSlot.
+ * Indicates whether or not there is a modem stack enabled for the given SIM slot.
*
* <p>Requires Permission:
- * READ_PRIVILEGED_PHONE_STATE or
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
+ * READ_PRIVILEGED_PHONE_STATE or that the calling app has carrier privileges (see
+ * {@link #hasCarrierPrivileges()}).
+ *
* @param slotIndex which slot it's checking.
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
public boolean isModemEnabledForSlot(int slotIndex) {
try {
ITelephony telephony = getITelephony();
diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS
index b5561010e7f9..c1221e3940d2 100644
--- a/tests/FlickerTests/OWNERS
+++ b/tests/FlickerTests/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 909476
include /services/core/java/com/android/server/wm/OWNERS
-natanieljr@google.com \ No newline at end of file
+natanieljr@google.com
+pablogamito@google.com
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8a2ddf1a243d..3c9ba20c340a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -196,7 +196,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
- @Presubmit
+ @Postsubmit
@Test
fun runPresubmitAssertion() {
flickerRule.checkPresubmitAssertions()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index eb7d29dfee74..4a904044cd74 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -95,7 +95,7 @@ class ChangeAppRotationTest(
}
}
- @Presubmit
+ @Postsubmit
@Test
fun runPresubmitAssertion() {
flickerRule.checkPresubmitAssertions()
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d2eda3293f0..a266b476bc0d 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -154,7 +154,7 @@ StringPiece GetFilename(const StringPiece& path) {
const char* end = path.end();
const char* last_dir_sep = path.begin();
for (const char* c = path.begin(); c != end; ++c) {
- if (*c == sDirSep) {
+ if (*c == sDirSep || *c == sInvariantDirSep) {
last_dir_sep = c + 1;
}
}
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 877cd56d6c69..a2b1b58e5d4f 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -41,6 +41,8 @@ constexpr const char sDirSep = '/';
constexpr const char sPathSep = ':';
#endif
+constexpr const char sInvariantDirSep = '/';
+
enum class FileType {
kUnknown = 0,
kNonExistant,