summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/accessibility/accessibility.aconfig17
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java60
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java25
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java132
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java55
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java32
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java67
-rw-r--r--services/appwidget/Android.bp10
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java41
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java6
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java6
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationDiskStore.java42
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationStore.java12
-rw-r--r--services/companion/java/com/android/server/companion/association/DisassociationProcessor.java21
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java56
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java54
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java96
-rw-r--r--services/core/java/com/android/server/SensitiveContentProtectionManagerService.java21
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java15
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java14
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java40
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java5
-rw-r--r--services/core/java/com/android/server/am/OWNERS4
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java26
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java3
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java1
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java39
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java73
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java281
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java4
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java29
-rw-r--r--services/core/java/com/android/server/backup/SystemBackupAgent.java8
-rw-r--r--services/core/java/com/android/server/backup/WearBackupHelper.java49
-rw-r--r--services/core/java/com/android/server/backup/WearBackupInternal.java32
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java18
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java16
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java18
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java61
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java12
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java33
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java3
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java27
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig25
-rw-r--r--services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java8
-rw-r--r--services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java14
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java8
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java5
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java38
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java75
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java22
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java13
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java31
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java28
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java74
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java6
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS1
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java3
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java26
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java140
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java293
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java230
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java25
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationShellCmd.java9
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java90
-rw-r--r--services/core/java/com/android/server/notification/ZenModeEventLogger.java117
-rw-r--r--services/core/java/com/android/server/notification/ZenModeFiltering.java11
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java622
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/pm/BackgroundInstallControlService.java35
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java59
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java11
-rw-r--r--services/core/java/com/android/server/pm/ShortcutLauncher.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java145
-rw-r--r--services/core/java/com/android/server/pm/ShortcutUser.java54
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java3
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java22
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java25
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java12
-rw-r--r--services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java13
-rw-r--r--services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java18
-rw-r--r--services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java6
-rw-r--r--services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java14
-rw-r--r--services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java33
-rw-r--r--services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java28
-rw-r--r--services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java29
-rw-r--r--services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java20
-rw-r--r--services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java27
-rw-r--r--services/core/java/com/android/server/power/stats/processor/MultiStateStats.java5
-rw-r--r--services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java33
-rw-r--r--services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java17
-rw-r--r--services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java18
-rw-r--r--services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java19
-rw-r--r--services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java27
-rw-r--r--services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java1
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java5
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java17
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java12
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java6
-rw-r--r--services/core/java/com/android/server/security/CertificateRevocationStatusManager.java45
-rw-r--r--services/core/java/com/android/server/security/FileIntegrityService.java5
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java2
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java4
-rw-r--r--services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java15
-rw-r--r--services/core/java/com/android/server/vibrator/VendorVibrationSession.java33
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java317
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java209
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java47
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java239
-rw-r--r--services/core/java/com/android/server/wm/AppCompatController.java8
-rw-r--r--services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java39
-rw-r--r--services/core/java/com/android/server/wm/AppCompatRoundedCorners.java8
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSandboxingPolicy.java65
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java12
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java21
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java19
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java12
-rw-r--r--services/core/java/com/android/server/wm/DeferredDisplayUpdater.java8
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeHelper.java38
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java156
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java16
-rw-r--r--services/core/java/com/android/server/wm/DragState.java55
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java24
-rw-r--r--services/core/java/com/android/server/wm/PresentationController.java10
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/Task.java17
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java7
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java5
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java13
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java74
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java230
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java46
-rw-r--r--services/core/java/com/android/server/wm/utils/WindowStyleCache.java81
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp37
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java69
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java63
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java9
-rw-r--r--services/java/com/android/server/SystemServer.java36
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java40
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java512
-rw-r--r--services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java6
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java43
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java107
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java11
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java68
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java23
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java21
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java102
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java19
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java86
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java22
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java36
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java43
-rw-r--r--services/tests/powerstatstests/res/raw/battery-history.zipbin0 -> 272063 bytes
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java182
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java10
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java2
-rw-r--r--services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java178
-rw-r--r--services/tests/servicestests/AndroidManifest.xml4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java209
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java115
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java210
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java10
-rw-r--r--services/tests/uiservicestests/Android.bp1
-rw-r--r--services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java12
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java24
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java496
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java221
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java87
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java22
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java7
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java57
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java88
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java795
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java35
-rw-r--r--services/tests/wmtests/res/values/styles.xml10
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java60
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java106
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java54
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java39
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/WindowStyleCacheTest.java63
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java6
244 files changed, 7489 insertions, 3882 deletions
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 529a564ea607..5e1fe8a60973 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -11,6 +11,16 @@ flag {
}
flag {
+ name: "allow_secure_screenshots"
+ namespace: "accessibility"
+ description: "Allow certain AccessibilityServices to take screenshots of FLAG_SECURE screens"
+ bug: "373705911"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "always_allow_observing_touch_events"
namespace: "accessibility"
description: "Always allows InputFilter observing SOURCE_TOUCHSCREEN events, even if touch exploration is enabled."
@@ -145,6 +155,13 @@ flag {
}
flag {
+ name: "enable_magnification_follows_mouse_with_pointer_motion_filter"
+ namespace: "accessibility"
+ description: "Whether to enable mouse following using pointer motion filter"
+ bug: "361817142"
+}
+
+flag {
name: "enable_magnification_keyboard_control"
namespace: "accessibility"
description: "Whether to enable keyboard control for magnification"
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 9ceca5d1dbfe..4b042489f3eb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -86,11 +86,13 @@ import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityCache;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.accessibility.IWindowSurfaceInfoCallback;
import android.view.inputmethod.EditorInfo;
import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
@@ -343,6 +345,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
SurfaceControl sc,
IAccessibilityInteractionConnectionCallback callback);
+ int performScreenCapture(ScreenCapture.LayerCaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener captureListener);
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
@@ -1456,7 +1460,45 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_WINDOW, interactionId);
return;
}
- connection.getRemote().takeScreenshotOfWindow(interactionId, listener, callback);
+ if (Flags.allowSecureScreenshots()) {
+ IWindowSurfaceInfoCallback infoCallback = new IWindowSurfaceInfoCallback.Stub() {
+ @Override
+ public void provideWindowSurfaceInfo(int windowFlags, int processUid,
+ SurfaceControl surfaceControl) {
+ final boolean canCaptureSecureLayers = canCaptureSecureLayers();
+ if (!canCaptureSecureLayers
+ && (windowFlags & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+ try {
+ callback.sendTakeScreenshotOfWindowError(
+ AccessibilityService.ERROR_TAKE_SCREENSHOT_SECURE_WINDOW,
+ interactionId);
+ } catch (RemoteException e) {
+ // ignore - the other side will time out
+ }
+ return;
+ }
+
+ final ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl)
+ .setChildrenOnly(false)
+ .setUid(processUid)
+ .setCaptureSecureLayers(canCaptureSecureLayers)
+ .build();
+ if (mSystemSupport.performScreenCapture(captureArgs, listener) != 0) {
+ try {
+ callback.sendTakeScreenshotOfWindowError(
+ AccessibilityService.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
+ interactionId);
+ } catch (RemoteException e) {
+ // ignore - the other side will time out
+ }
+ }
+ }
+ };
+ connection.getRemote().getWindowSurfaceInfo(infoCallback);
+ } else {
+ connection.getRemote().takeScreenshotOfWindow(interactionId, listener, callback);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1523,7 +1565,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
);
- mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
+ if (Flags.allowSecureScreenshots()) {
+ mWindowManagerService.captureDisplay(displayId,
+ new ScreenCapture.CaptureArgs.Builder<>()
+ .setCaptureSecureLayers(canCaptureSecureLayers()).build(),
+ screenCaptureListener);
+ } else {
+ mWindowManagerService.captureDisplay(displayId, null, screenCaptureListener);
+ }
} catch (Exception e) {
sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
callback);
@@ -1564,6 +1613,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}, null).recycleOnUse());
}
+ private boolean canCaptureSecureLayers() {
+ return Flags.allowSecureScreenshots()
+ && mAccessibilityServiceInfo.isAccessibilityTool()
+ && mAccessibilityServiceInfo.getResolveInfo().serviceInfo
+ .applicationInfo.isSystemApp();
+ }
+
@Override
@PermissionManuallyEnforced
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index e422fef6c22c..9b5f22afb81d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -134,7 +134,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*/
static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x00000080;
-/**
+ /**
* Flag for enabling multi-finger gestures.
*
* @see #setUserAndEnabledFeatures(int, int)
@@ -190,8 +190,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private final AccessibilityManagerService mAms;
- private final InputManager mInputManager;
-
private final SparseArray<EventStreamTransformation> mEventHandler;
private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);
@@ -294,7 +292,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mContext = context;
mAms = service;
mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mInputManager = context.getSystemService(InputManager.class);
mEventHandler = eventHandler;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 67fdca446ba4..9eb8442be783 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -173,6 +173,7 @@ import android.view.accessibility.IAccessibilityManagerClient;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IUserInitializationCompleteCallback;
import android.view.inputmethod.EditorInfo;
+import android.window.ScreenCapture;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -6725,6 +6726,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
callback));
}
+ @Override
+ public int performScreenCapture(ScreenCapture.LayerCaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener captureListener) {
+ return ScreenCapture.captureLayers(captureArgs, captureListener);
+ }
+
@VisibleForTesting
int getShortcutTypeForGenericShortcutCalls(int userId) {
int navigationMode = Settings.Secure.getIntForUser(
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 5283df5ca7e1..aa82df493f84 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -101,8 +101,15 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
@Override
- public void toggleAutoclickPause() {
- // TODO(b/388872274): allows users to pause the autoclick.
+ public void toggleAutoclickPause(boolean paused) {
+ if (paused) {
+ if (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.cancel();
+ }
+ }
}
};
@@ -133,7 +140,9 @@ public class AutoclickController extends BaseEventStreamTransformation {
mAutoclickIndicatorScheduler);
}
- handleMouseMotion(event, policyFlags);
+ if (!isPaused()) {
+ handleMouseMotion(event, policyFlags);
+ }
} else if (mClickScheduler != null) {
mClickScheduler.cancel();
}
@@ -216,6 +225,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
}
+ private boolean isPaused() {
+ // TODO (b/397460424): Unpause when hovering over panel.
+ return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused();
+ }
+
/**
* Observes autoclick setting values, and updates ClickScheduler delay and indicator size
* whenever the setting value changes.
@@ -538,6 +552,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
return mActive;
}
+ @VisibleForTesting
+ long getScheduledClickTimeForTesting() {
+ return mScheduledClickTime;
+ }
+
/**
* Updates delay that should be used when scheduling clicks. The delay will be used only for
* clicks scheduled after this point (pending click tasks are not affected).
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index 614b2285d6e0..ba3e3d14b9c6 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -46,6 +46,11 @@ public class AutoclickTypePanel {
public static final int AUTOCLICK_TYPE_DRAG = 3;
public static final int AUTOCLICK_TYPE_SCROLL = 4;
+ public static final int CORNER_BOTTOM_RIGHT = 0;
+ public static final int CORNER_BOTTOM_LEFT = 1;
+ public static final int CORNER_TOP_LEFT = 2;
+ public static final int CORNER_TOP_RIGHT = 3;
+
// Types of click the AutoclickTypePanel supports.
@IntDef({
AUTOCLICK_TYPE_LEFT_CLICK,
@@ -56,14 +61,38 @@ public class AutoclickTypePanel {
})
public @interface AutoclickType {}
+ @IntDef({
+ CORNER_BOTTOM_RIGHT,
+ CORNER_BOTTOM_LEFT,
+ CORNER_TOP_LEFT,
+ CORNER_TOP_RIGHT
+ })
+ public @interface Corner {}
+
+ private static final @Corner int[] CORNER_ROTATION_ORDER = {
+ CORNER_BOTTOM_RIGHT,
+ CORNER_BOTTOM_LEFT,
+ CORNER_TOP_LEFT,
+ CORNER_TOP_RIGHT
+ };
+
// An interface exposed to {@link AutoclickController) to handle different actions on the panel,
// including changing autoclick type, pausing/resuming autoclick.
public interface ClickPanelControllerInterface {
- // Allows users to change a different autoclick type.
+ /**
+ * Allows users to change a different autoclick type.
+ *
+ * @param clickType The new autoclick type to use. Should be one of the values defined in
+ * {@link AutoclickType}.
+ */
void handleAutoclickTypeChange(@AutoclickType int clickType);
- // Allows users to pause/resume the autoclick.
- void toggleAutoclickPause();
+ /**
+ * Allows users to pause or resume autoclick.
+ *
+ * @param paused {@code true} to pause autoclick, {@code false} to resume.
+ */
+ void toggleAutoclickPause(boolean paused);
}
private final Context mContext;
@@ -77,14 +106,26 @@ public class AutoclickTypePanel {
// Whether the panel is expanded or not.
private boolean mExpanded = false;
+ // Whether autoclick is paused.
+ private boolean mPaused = false;
+ // Tracks the current corner position of the panel using an index into CORNER_ROTATION_ORDER
+ // array. This allows the panel to cycle through screen corners in a defined sequence when
+ // repositioned.
+ private int mCurrentCornerIndex = 0;
+
private final LinearLayout mLeftClickButton;
private final LinearLayout mRightClickButton;
private final LinearLayout mDoubleClickButton;
private final LinearLayout mDragButton;
private final LinearLayout mScrollButton;
+ private final LinearLayout mPauseButton;
+ private final LinearLayout mPositionButton;
private LinearLayout mSelectedButton;
+ private final Drawable mPauseButtonDrawable;
+ private final Drawable mResumeButtonDrawable;
+
public AutoclickTypePanel(
Context context,
WindowManager windowManager,
@@ -93,6 +134,11 @@ public class AutoclickTypePanel {
mWindowManager = windowManager;
mClickPanelController = clickPanelController;
+ mPauseButtonDrawable = mContext.getDrawable(
+ R.drawable.accessibility_autoclick_pause);
+ mResumeButtonDrawable = mContext.getDrawable(
+ R.drawable.accessibility_autoclick_resume);
+
mContentView =
LayoutInflater.from(context)
.inflate(R.layout.accessibility_autoclick_type_panel, null);
@@ -104,6 +150,8 @@ public class AutoclickTypePanel {
mContentView.findViewById(R.id.accessibility_autoclick_double_click_layout);
mScrollButton = mContentView.findViewById(R.id.accessibility_autoclick_scroll_layout);
mDragButton = mContentView.findViewById(R.id.accessibility_autoclick_drag_layout);
+ mPauseButton = mContentView.findViewById(R.id.accessibility_autoclick_pause_layout);
+ mPositionButton = mContentView.findViewById(R.id.accessibility_autoclick_position_layout);
initializeButtonState();
}
@@ -115,11 +163,8 @@ public class AutoclickTypePanel {
v -> togglePanelExpansion(AUTOCLICK_TYPE_DOUBLE_CLICK));
mScrollButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_SCROLL));
mDragButton.setOnClickListener(v -> togglePanelExpansion(AUTOCLICK_TYPE_DRAG));
-
- // TODO(b/388872274): registers listener for pause button and allows users to pause/resume
- // the autoclick.
- // TODO(b/388847771): registers listener for position button and allows users to move the
- // panel to a different position.
+ mPositionButton.setOnClickListener(v -> moveToNextCorner());
+ mPauseButton.setOnClickListener(v -> togglePause());
// Initializes panel as collapsed state and only displays the left click button.
hideAllClickTypeButtons();
@@ -175,6 +220,10 @@ public class AutoclickTypePanel {
mWindowManager.removeView(mContentView);
}
+ public boolean isPaused() {
+ return mPaused;
+ }
+
/** Toggles the panel expanded or collapsed state. */
private void togglePanelExpansion(@AutoclickType int clickType) {
final LinearLayout button = getButtonFromClickType(clickType);
@@ -196,6 +245,18 @@ public class AutoclickTypePanel {
mExpanded = !mExpanded;
}
+ private void togglePause() {
+ mPaused = !mPaused;
+ mClickPanelController.toggleAutoclickPause(mPaused);
+
+ ImageButton imageButton = (ImageButton) mPauseButton.getChildAt(/* index= */ 0);
+ if (mPaused) {
+ imageButton.setImageDrawable(mResumeButtonDrawable);
+ } else {
+ imageButton.setImageDrawable(mPauseButtonDrawable);
+ }
+ }
+
/** Hide all buttons on the panel except pause and position buttons. */
private void hideAllClickTypeButtons() {
mLeftClickButton.setVisibility(View.GONE);
@@ -225,6 +286,46 @@ public class AutoclickTypePanel {
};
}
+ /** Moves the panel to the next corner in clockwise direction. */
+ private void moveToNextCorner() {
+ @Corner int nextCornerIndex = (mCurrentCornerIndex + 1) % CORNER_ROTATION_ORDER.length;
+ mCurrentCornerIndex = nextCornerIndex;
+
+ // getLayoutParams() will update the panel position based on current corner.
+ WindowManager.LayoutParams params = getLayoutParams();
+ mWindowManager.updateViewLayout(mContentView, params);
+ }
+
+ private void setPanelPositionForCorner(WindowManager.LayoutParams params, @Corner int corner) {
+ // TODO(b/396402941): Replace hardcoded pixel values with proper dimension calculations,
+ // Current values are experimental and may not work correctly across different device
+ // resolutions and configurations.
+ switch (corner) {
+ case CORNER_BOTTOM_RIGHT:
+ params.gravity = Gravity.END | Gravity.BOTTOM;
+ params.x = 15;
+ params.y = 90;
+ break;
+ case CORNER_BOTTOM_LEFT:
+ params.gravity = Gravity.START | Gravity.BOTTOM;
+ params.x = 15;
+ params.y = 90;
+ break;
+ case CORNER_TOP_LEFT:
+ params.gravity = Gravity.START | Gravity.TOP;
+ params.x = 15;
+ params.y = 30;
+ break;
+ case CORNER_TOP_RIGHT:
+ params.gravity = Gravity.END | Gravity.TOP;
+ params.x = 15;
+ params.y = 30;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid corner: " + corner);
+ }
+ }
+
@VisibleForTesting
boolean getExpansionStateForTesting() {
return mExpanded;
@@ -236,12 +337,19 @@ public class AutoclickTypePanel {
return mContentView;
}
+ @VisibleForTesting
+ @Corner
+ int getCurrentCornerIndexForTesting() {
+ return mCurrentCornerIndex;
+ }
+
/**
* Retrieves the layout params for AutoclickIndicatorView, used when it's added to the Window
* Manager.
*/
+ @VisibleForTesting
@NonNull
- private WindowManager.LayoutParams getLayoutParams() {
+ WindowManager.LayoutParams getLayoutParams() {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -254,11 +362,7 @@ public class AutoclickTypePanel {
mContext.getString(R.string.accessibility_autoclick_type_settings_panel_title);
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
- // TODO(b/388847771): Compute position based on user interaction.
- layoutParams.x = 15;
- layoutParams.y = 90;
- layoutParams.gravity = Gravity.END | Gravity.BOTTOM;
-
+ setPanelPositionForCorner(layoutParams, mCurrentCornerIndex);
return layoutParams;
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index d0ee7af1bbfb..5191fb5f51cb 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -20,6 +20,8 @@ import static android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_R
import static android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_NAMESPACE;
import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR;
+import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION;
+import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FUNCTIONS_DENIED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -236,30 +238,42 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
requestInternal.getCallingPackage(),
targetPackageName,
requestInternal.getClientRequest().getFunctionIdentifier())
- .thenAccept(
- canExecute -> {
- if (!canExecute) {
- throw new SecurityException(
+ .thenCompose(
+ canExecuteResult -> {
+ if (canExecuteResult == CAN_EXECUTE_APP_FUNCTIONS_DENIED) {
+ return AndroidFuture.failedFuture(new SecurityException(
"Caller does not have permission to execute the"
- + " appfunction");
+ + " appfunction"));
}
+ return isAppFunctionEnabled(
+ requestInternal
+ .getClientRequest()
+ .getFunctionIdentifier(),
+ requestInternal
+ .getClientRequest()
+ .getTargetPackageName(),
+ getAppSearchManagerAsUser(
+ requestInternal.getUserHandle()),
+ THREAD_POOL_EXECUTOR)
+ .thenApply(
+ isEnabled -> {
+ if (!isEnabled) {
+ throw new DisabledAppFunctionException(
+ "The app function is disabled");
+ }
+ return canExecuteResult;
+ });
})
- .thenCompose(
- isEnabled ->
- isAppFunctionEnabled(
- requestInternal.getClientRequest().getFunctionIdentifier(),
- requestInternal.getClientRequest().getTargetPackageName(),
- getAppSearchManagerAsUser(requestInternal.getUserHandle()),
- THREAD_POOL_EXECUTOR))
.thenAccept(
- isEnabled -> {
- if (!isEnabled) {
- throw new DisabledAppFunctionException(
- "The app function is disabled");
+ canExecuteResult -> {
+ int bindFlags = Context.BIND_AUTO_CREATE;
+ if (canExecuteResult
+ == CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION) {
+ // If the caller doesn't have the permission, do not use
+ // BIND_FOREGROUND_SERVICE to avoid it raising its process state by
+ // calling its own AppFunctions.
+ bindFlags |= Context.BIND_FOREGROUND_SERVICE;
}
- })
- .thenAccept(
- unused -> {
Intent serviceIntent =
mInternalServiceHelper.resolveAppFunctionService(
targetPackageName, targetUser);
@@ -294,8 +308,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
targetUser,
localCancelTransport,
safeExecuteAppFunctionCallback,
- /* bindFlags= */ Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE,
+ bindFlags,
callerBinder,
callingUid);
})
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 98ef974b9443..c8038a4e56df 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -17,12 +17,16 @@
package com.android.server.appfunctions;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Interface for validating that the caller has the correct privilege to call an AppFunctionManager
* API.
@@ -70,7 +74,8 @@ public interface CallerValidator {
* @param functionId The id of the app function to execute.
* @return Whether the caller can execute the specified app function.
*/
- AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+ @CanExecuteAppFunctionResult
+ AndroidFuture<Integer> verifyCallerCanExecuteAppFunction(
int callingUid,
int callingPid,
@NonNull UserHandle targetUser,
@@ -78,6 +83,31 @@ public interface CallerValidator {
@NonNull String targetPackageName,
@NonNull String functionId);
+ @IntDef(
+ prefix = {"CAN_EXECUTE_APP_FUNCTIONS_"},
+ value = {
+ CAN_EXECUTE_APP_FUNCTIONS_DENIED,
+ CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_SAME_PACKAGE,
+ CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CanExecuteAppFunctionResult {}
+
+ /** Callers are not allowed to execute app functions. */
+ int CAN_EXECUTE_APP_FUNCTIONS_DENIED = 0;
+
+ /**
+ * Callers can execute app functions because they are calling app functions from the same
+ * package.
+ */
+ int CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_SAME_PACKAGE = 1;
+
+ /**
+ * Callers can execute app functions because they have the necessary permission.
+ * This case also applies when a caller with the permission invokes their own app functions.
+ */
+ int CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION = 2;
+
/**
* Checks if the app function policy is allowed.
*
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index fe163d77c4fc..3f8b2e3316dc 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -16,24 +16,12 @@
package com.android.server.appfunctions;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_METADATA_DB;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_NAMESPACE;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.getDocumentIdForAppFunction;
-
-import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR;
-
import android.Manifest;
import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.AppFunctionsPolicy;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchManager.SearchContext;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.GetByDocumentIdRequest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -84,64 +72,25 @@ class CallerValidatorImpl implements CallerValidator {
@Override
@RequiresPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS)
- public AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+ @CanExecuteAppFunctionResult
+ public AndroidFuture<Integer> verifyCallerCanExecuteAppFunction(
int callingUid,
int callingPid,
@NonNull UserHandle targetUser,
@NonNull String callerPackageName,
@NonNull String targetPackageName,
@NonNull String functionId) {
- if (callerPackageName.equals(targetPackageName)) {
- return AndroidFuture.completedFuture(true);
- }
-
boolean hasExecutionPermission =
mContext.checkPermission(
- Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
+ Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
-
- if (!hasExecutionPermission) {
- return AndroidFuture.completedFuture(false);
+ if (hasExecutionPermission) {
+ return AndroidFuture.completedFuture(CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION);
}
-
- FutureAppSearchSession futureAppSearchSession =
- new FutureAppSearchSessionImpl(
- Objects.requireNonNull(
- mContext.createContextAsUser(targetUser, 0)
- .getSystemService(AppSearchManager.class)),
- THREAD_POOL_EXECUTOR,
- new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
-
- String documentId = getDocumentIdForAppFunction(targetPackageName, functionId);
-
- return futureAppSearchSession
- .getByDocumentId(
- new GetByDocumentIdRequest.Builder(APP_FUNCTION_STATIC_NAMESPACE)
- .addIds(documentId)
- .build())
- .thenApply(
- batchResult -> getGenericDocumentFromBatchResult(batchResult, documentId))
- // At this point, already checked the app has the permission.
- .thenApply(document -> true)
- .whenComplete(
- (result, throwable) -> {
- futureAppSearchSession.close();
- });
- }
-
- private static GenericDocument getGenericDocumentFromBatchResult(
- AppSearchBatchResult<String, GenericDocument> result, String documentId) {
- if (result.isSuccess()) {
- return result.getSuccesses().get(documentId);
+ if (callerPackageName.equals(targetPackageName)) {
+ return AndroidFuture.completedFuture(CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_SAME_PACKAGE);
}
-
- AppSearchResult<GenericDocument> failedResult = result.getFailures().get(documentId);
- throw new AppSearchException(
- failedResult.getResultCode(),
- "Unable to retrieve document with id: "
- + documentId
- + " due to "
- + failedResult.getErrorMessage());
+ return AndroidFuture.completedFuture(CAN_EXECUTE_APP_FUNCTIONS_DENIED);
}
@Override
diff --git a/services/appwidget/Android.bp b/services/appwidget/Android.bp
index 8119073fdf7f..9548afe398b1 100644
--- a/services/appwidget/Android.bp
+++ b/services/appwidget/Android.bp
@@ -17,6 +17,12 @@ filegroup {
java_library_static {
name: "services.appwidget",
defaults: ["platform_service_defaults"],
- srcs: [":services.appwidget-sources"],
- libs: ["services.core"],
+ srcs: [
+ ":services.appwidget-sources",
+ ":statslog-framework-java-gen",
+ ],
+ libs: [
+ "androidx.annotation_annotation",
+ "services.core",
+ ],
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index e0f2939a2083..74a87ed92f52 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -50,6 +50,7 @@ import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.KeyguardManager;
import android.app.PendingIntent;
+import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
import android.app.usage.Flags;
@@ -124,6 +125,7 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.StatsEvent;
import android.util.TypedValue;
import android.util.Xml;
import android.util.proto.ProtoInputStream;
@@ -145,6 +147,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -433,6 +436,44 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+ registerPullCallbacks();
+ }
+
+ /**
+ * Register callbacks for pull atoms.
+ */
+ private void registerPullCallbacks() {
+ final StatsManager manager = mContext.getSystemService(StatsManager.class);
+ manager.setPullAtomCallback(FrameworkStatsLog.WIDGET_MEMORY_STATS,
+ new StatsManager.PullAtomMetadata.Builder().build(),
+ new HandlerExecutor(mCallbackHandler), this::onPullAtom);
+ }
+
+ /**
+ * Callback from StatsManager to log events indicated by the atomTag. This function will add
+ * the relevant events to the data list.
+ *
+ * @return PULL_SUCCESS if the pull was successful and events should be used, else PULL_SKIP.
+ */
+ private int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+ if (atomTag == FrameworkStatsLog.WIDGET_MEMORY_STATS) {
+ synchronized (mLock) {
+ for (Widget widget : mWidgets) {
+ if (widget.views != null) {
+ final int uid = widget.provider.id.uid;
+ final int appWidgetId = widget.appWidgetId;
+ final long bitmapMemoryUsage =
+ widget.views.estimateTotalBitmapMemoryUsage();
+ StatsEvent event = FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.WIDGET_MEMORY_STATS, uid, appWidgetId,
+ bitmapMemoryUsage);
+ data.add(event);
+ }
+ }
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+ return StatsManager.PULL_SKIP;
}
/**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 05301fdd8385..4f56483f487e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -31,6 +31,8 @@ import static android.os.UserHandle.getCallingUserId;
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.companion.association.DisassociationProcessor.REASON_API;
+import static com.android.server.companion.association.DisassociationProcessor.REASON_PKG_DATA_CLEARED;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
@@ -250,7 +252,7 @@ public class CompanionDeviceManagerService extends SystemService {
+ packageName + "]. Cleaning up CDM data...");
for (AssociationInfo association : associationsForPackage) {
- mDisassociationProcessor.disassociate(association.getId());
+ mDisassociationProcessor.disassociate(association.getId(), REASON_PKG_DATA_CLEARED);
}
mCompanionAppBinder.onPackageChanged(userId);
@@ -426,7 +428,7 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public void disassociate(int associationId) {
- mDisassociationProcessor.disassociate(associationId);
+ mDisassociationProcessor.disassociate(associationId, REASON_API);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index e7d1460aa66a..c5ac7c31b5c3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,6 +18,8 @@ package com.android.server.companion;
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
+import static com.android.server.companion.association.DisassociationProcessor.REASON_SHELL;
+
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
import android.companion.Flags;
@@ -122,7 +124,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
if (association == null) {
out.println("Association doesn't exist.");
} else {
- mDisassociationProcessor.disassociate(association.getId());
+ mDisassociationProcessor.disassociate(association.getId(), REASON_SHELL);
}
}
break;
@@ -132,7 +134,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
final List<AssociationInfo> userAssociations =
mAssociationStore.getAssociationsByUser(userId);
for (AssociationInfo association : userAssociations) {
- mDisassociationProcessor.disassociate(association.getId());
+ mDisassociationProcessor.disassociate(association.getId(), REASON_SHELL);
}
}
break;
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index f2d019bde703..ce7dcd0fa1d4 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -58,6 +58,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -164,6 +165,7 @@ public final class AssociationDiskStore {
private static final String FILE_NAME_LEGACY = "companion_device_manager_associations.xml";
private static final String FILE_NAME = "companion_device_manager.xml";
+ private static final String FILE_NAME_LAST_REMOVED_ASSOCIATION = "last_removed_association.txt";
private static final String XML_TAG_STATE = "state";
private static final String XML_TAG_ASSOCIATIONS = "associations";
@@ -268,6 +270,46 @@ public final class AssociationDiskStore {
}
}
+ /**
+ * Read the last removed association from disk.
+ */
+ public String readLastRemovedAssociation(@UserIdInt int userId) {
+ final AtomicFile file = createStorageFileForUser(
+ userId, FILE_NAME_LAST_REMOVED_ASSOCIATION);
+ StringBuilder sb = new StringBuilder();
+ int c;
+ try (FileInputStream fis = file.openRead()) {
+ while ((c = fis.read()) != -1) {
+ sb.append((char) c);
+ }
+ fis.close();
+ return sb.toString();
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "File " + file + " for user=" + userId + " doesn't exist.");
+ return null;
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't read file " + file + " for user=" + userId);
+ return null;
+ }
+ }
+
+ /**
+ * Write the last removed association to disk.
+ */
+ public void writeLastRemovedAssociation(AssociationInfo association, String reason) {
+ Slog.i(TAG, "Writing last removed association=" + association.getId() + " to disk...");
+
+ final AtomicFile file = createStorageFileForUser(
+ association.getUserId(), FILE_NAME_LAST_REMOVED_ASSOCIATION);
+ writeToFileSafely(file, out -> {
+ out.write(String.valueOf(System.currentTimeMillis()).getBytes());
+ out.write(' ');
+ out.write(reason.getBytes());
+ out.write(' ');
+ out.write(association.toString().getBytes());
+ });
+ }
+
@NonNull
private static Associations readAssociationsFromFile(@UserIdInt int userId,
@NonNull AtomicFile file, @NonNull String rootTag) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationStore.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java
index 757abd927ac8..f70c434e6b46 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java
@@ -276,7 +276,7 @@ public class AssociationStore {
/**
* Remove an association.
*/
- public void removeAssociation(int id) {
+ public void removeAssociation(int id, String reason) {
Slog.i(TAG, "Removing association id=[" + id + "]...");
final AssociationInfo association;
@@ -291,6 +291,8 @@ public class AssociationStore {
writeCacheToDisk(association.getUserId());
+ mDiskStore.writeLastRemovedAssociation(association, reason);
+
Slog.i(TAG, "Done removing association.");
}
@@ -525,6 +527,14 @@ public class AssociationStore {
out.append(" ").append(a.toString()).append('\n');
}
}
+
+ out.append("Last Removed Association:\n");
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ String lastRemovedAssociation = mDiskStore.readLastRemovedAssociation(user.id);
+ if (lastRemovedAssociation != null) {
+ out.append(" ").append(lastRemovedAssociation).append('\n');
+ }
+ }
}
private void broadcastChange(@ChangeType int changeType, AssociationInfo association) {
diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
index 150e8da5f614..248056f32a4f 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -47,6 +47,13 @@ import com.android.server.companion.transport.CompanionTransportManager;
@SuppressLint("LongLogTag")
public class DisassociationProcessor {
+ public static final String REASON_REVOKED = "revoked";
+ public static final String REASON_SELF_IDLE = "self-idle";
+ public static final String REASON_SHELL = "shell";
+ public static final String REASON_LEGACY = "legacy";
+ public static final String REASON_API = "api";
+ public static final String REASON_PKG_DATA_CLEARED = "pkg-data-cleared";
+
private static final String TAG = "CDM_DisassociationProcessor";
private static final String SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW =
@@ -94,7 +101,7 @@ public class DisassociationProcessor {
* Disassociate an association by id.
*/
// TODO: also revoke notification access
- public void disassociate(int id) {
+ public void disassociate(int id, String reason) {
Slog.i(TAG, "Disassociating id=[" + id + "]...");
final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(id);
@@ -126,7 +133,7 @@ public class DisassociationProcessor {
// Association cleanup.
mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, id);
- mAssociationStore.removeAssociation(association.getId());
+ mAssociationStore.removeAssociation(association.getId(), reason);
// If role is not in use by other associations, revoke the role.
// Do not need to remove the system role since it was pre-granted by the system.
@@ -151,7 +158,7 @@ public class DisassociationProcessor {
}
/**
- * @deprecated Use {@link #disassociate(int)} instead.
+ * @deprecated Use {@link #disassociate(int, String)} instead.
*/
@Deprecated
public void disassociate(int userId, String packageName, String macAddress) {
@@ -165,7 +172,7 @@ public class DisassociationProcessor {
mAssociationStore.getAssociationWithCallerChecks(association.getId());
- disassociate(association.getId());
+ disassociate(association.getId(), REASON_LEGACY);
}
@SuppressLint("MissingPermission")
@@ -223,7 +230,7 @@ public class DisassociationProcessor {
Slog.i(TAG, "Removing inactive self-managed association=[" + association.toShortString()
+ "].");
- disassociate(id);
+ disassociate(id, REASON_SELF_IDLE);
}
}
@@ -234,7 +241,7 @@ public class DisassociationProcessor {
*
* Lastly remove the role holder for the revoked associations for the same packages.
*
- * @see #disassociate(int)
+ * @see #disassociate(int, String)
*/
private class OnPackageVisibilityChangeListener implements
ActivityManager.OnUidImportanceListener {
@@ -260,7 +267,7 @@ public class DisassociationProcessor {
int userId = UserHandle.getUserId(uid);
for (AssociationInfo association : mAssociationStore.getRevokedAssociations(userId,
packageName)) {
- disassociate(association.getId());
+ disassociate(association.getId(), REASON_REVOKED);
}
if (mAssociationStore.getRevokedAssociations().isEmpty()) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index f03e8c713228..28efdfc01ee2 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -163,6 +163,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
*/
private static final long PENDING_TRAMPOLINE_TIMEOUT_MS = 5000;
+ /**
+ * Global lock for this Virtual Device.
+ *
+ * Never call outside this class while holding this lock. A number of other system services like
+ * WindowManager, DisplayManager, etc. call into this device to get device-specific information,
+ * while holding their own global locks.
+ *
+ * Making a call to another service while holding this lock creates lock order inversion and
+ * will potentially cause a deadlock.
+ */
private final Object mVirtualDeviceLock = new Object();
private final int mBaseVirtualDisplayFlags;
@@ -179,14 +189,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final int mDeviceId;
@Nullable
private final String mPersistentDeviceId;
- // Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
- // Holding the lock can lead to lock inversion with GlobalWindowManagerLock.
- // 1. After display is created the window manager calls into VDM during construction
- // of display specific context to fetch device id corresponding to the display.
- // mVirtualDeviceLock will be held while this is done.
- // 2. InputController interactions result in calls to DisplayManager (to set IME,
- // possibly more indirect calls), and those attempt to lock GlobalWindowManagerLock which
- // creates lock inversion.
private final InputController mInputController;
private final SensorController mSensorController;
private final CameraAccessController mCameraAccessController;
@@ -205,19 +207,23 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final DisplayManagerGlobal mDisplayManager;
private final DisplayManagerInternal mDisplayManagerInternal;
private final PowerManager mPowerManager;
- @GuardedBy("mVirtualDeviceLock")
+ @GuardedBy("mIntentInterceptors")
private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
@NonNull
private final Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
+
// The default setting for showing the pointer on new displays.
@GuardedBy("mVirtualDeviceLock")
private boolean mDefaultShowPointerIcon = true;
@GuardedBy("mVirtualDeviceLock")
@Nullable
private LocaleList mLocaleList = null;
- @GuardedBy("mVirtualDeviceLock")
+
+ // Lock for power operations for this virtual device that allow calling PowerManager.
+ private final Object mPowerLock = new Object();
+ @GuardedBy("mPowerLock")
private boolean mLockdownActive = false;
- @GuardedBy("mVirtualDeviceLock")
+ @GuardedBy("mPowerLock")
private boolean mRequestedToBeAwake = true;
@NonNull
@@ -334,7 +340,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
*/
@Override
public boolean shouldInterceptIntent(@NonNull Intent intent) {
- synchronized (mVirtualDeviceLock) {
+ synchronized (mIntentInterceptors) {
boolean hasInterceptedIntent = false;
for (Map.Entry<IBinder, IntentFilter> interceptor
: mIntentInterceptors.entrySet()) {
@@ -500,7 +506,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
void onLockdownChanged(boolean lockdownActive) {
- synchronized (mVirtualDeviceLock) {
+ synchronized (mPowerLock) {
if (lockdownActive != mLockdownActive) {
mLockdownActive = lockdownActive;
if (mLockdownActive) {
@@ -610,7 +616,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public void goToSleep() {
checkCallerIsDeviceOwner();
- synchronized (mVirtualDeviceLock) {
+ synchronized (mPowerLock) {
mRequestedToBeAwake = false;
}
final long ident = Binder.clearCallingIdentity();
@@ -624,7 +630,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@Override // Binder call
public void wakeUp() {
checkCallerIsDeviceOwner();
- synchronized (mVirtualDeviceLock) {
+ synchronized (mPowerLock) {
mRequestedToBeAwake = true;
if (mLockdownActive) {
Slog.w(TAG, "Cannot wake up device during lockdown.");
@@ -850,8 +856,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
checkDisplayOwnedByVirtualDeviceLocked(displayId);
if (mVirtualAudioController == null) {
mVirtualAudioController = new VirtualAudioController(mContext, mAttributionSource);
- GenericWindowPolicyController gwpc = mVirtualDisplays.get(
- displayId).getWindowPolicyController();
+ GenericWindowPolicyController gwpc =
+ mVirtualDisplays.get(displayId).getWindowPolicyController();
mVirtualAudioController.startListening(gwpc, routingCallback,
configChangedCallback);
}
@@ -1293,7 +1299,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
checkCallerIsDeviceOwner();
Objects.requireNonNull(intentInterceptor);
Objects.requireNonNull(filter);
- synchronized (mVirtualDeviceLock) {
+ synchronized (mIntentInterceptors) {
mIntentInterceptors.put(intentInterceptor.asBinder(), filter);
}
}
@@ -1303,7 +1309,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
@NonNull IVirtualDeviceIntentInterceptor intentInterceptor) {
checkCallerIsDeviceOwner();
Objects.requireNonNull(intentInterceptor);
- synchronized (mVirtualDeviceLock) {
+ synchronized (mIntentInterceptors) {
mIntentInterceptors.remove(intentInterceptor.asBinder());
}
}
@@ -1653,6 +1659,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
void goToSleepInternal(@PowerManager.GoToSleepReason int reason) {
final long now = SystemClock.uptimeMillis();
+ IntArray displayIds = new IntArray();
synchronized (mVirtualDeviceLock) {
for (int i = 0; i < mVirtualDisplays.size(); i++) {
VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
@@ -1660,13 +1667,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
continue;
}
int displayId = mVirtualDisplays.keyAt(i);
- mPowerManager.goToSleep(displayId, now, reason, /* flags= */ 0);
+ displayIds.add(displayId);
}
}
+ for (int i = 0; i < displayIds.size(); ++i) {
+ mPowerManager.goToSleep(displayIds.get(i), now, reason, /* flags= */ 0);
+ }
}
void wakeUpInternal(@PowerManager.WakeReason int reason, String details) {
final long now = SystemClock.uptimeMillis();
+ IntArray displayIds = new IntArray();
synchronized (mVirtualDeviceLock) {
for (int i = 0; i < mVirtualDisplays.size(); i++) {
VirtualDisplayWrapper wrapper = mVirtualDisplays.valueAt(i);
@@ -1674,9 +1685,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
continue;
}
int displayId = mVirtualDisplays.keyAt(i);
- mPowerManager.wakeUp(now, reason, details, displayId);
+ displayIds.add(displayId);
}
}
+ for (int i = 0; i < displayIds.size(); ++i) {
+ mPowerManager.wakeUp(now, reason, details, displayIds.get(i));
+ }
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index ff82ca00b840..93b4de856463 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -117,7 +117,18 @@ public class VirtualDeviceManagerService extends SystemService {
*/
static final int CDM_ASSOCIATION_ID_NONE = 0;
+ /**
+ * Global VDM lock.
+ *
+ * Never call outside this class while holding this lock. A number of other system services like
+ * WindowManager, DisplayManager, etc. call into VDM to get device-specific information, while
+ * holding their own global locks.
+ *
+ * Making a call to another service while holding this lock creates lock order inversion and
+ * will potentially cause a deadlock.
+ */
private final Object mVirtualDeviceManagerLock = new Object();
+
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerNativeImpl mNativeImpl;
private final VirtualDeviceManagerInternal mLocalService;
@@ -235,10 +246,9 @@ public class VirtualDeviceManagerService extends SystemService {
// Called when the global lockdown state changes, i.e. lockdown is considered active if any user
// is in lockdown mode, and inactive if no users are in lockdown mode.
void onLockdownChanged(boolean lockdownActive) {
- synchronized (mVirtualDeviceManagerLock) {
- for (int i = 0; i < mVirtualDevices.size(); i++) {
- mVirtualDevices.valueAt(i).onLockdownChanged(lockdownActive);
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ virtualDevicesSnapshot.get(i).onLockdownChanged(lockdownActive);
}
}
@@ -264,16 +274,14 @@ public class VirtualDeviceManagerService extends SystemService {
return null;
}
int userId = userHandle.getIdentifier();
- synchronized (mVirtualDeviceManagerLock) {
- for (int i = 0; i < mVirtualDevices.size(); i++) {
- final CameraAccessController cameraAccessController =
- mVirtualDevices.valueAt(i).getCameraAccessController();
- if (cameraAccessController.getUserId() == userId) {
- return cameraAccessController;
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ final CameraAccessController cameraAccessController =
+ virtualDevicesSnapshot.get(i).getCameraAccessController();
+ if (cameraAccessController.getUserId() == userId) {
+ return cameraAccessController;
}
- }
- Context userContext = getContext().createContextAsUser(userHandle, 0);
+ } Context userContext = getContext().createContextAsUser(userHandle, 0);
return new CameraAccessController(userContext, mLocalService, this::onCameraAccessBlocked);
}
@@ -520,11 +528,10 @@ public class VirtualDeviceManagerService extends SystemService {
@Override // Binder call
public List<VirtualDevice> getVirtualDevices() {
List<VirtualDevice> virtualDevices = new ArrayList<>();
- synchronized (mVirtualDeviceManagerLock) {
- for (int i = 0; i < mVirtualDevices.size(); i++) {
- final VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
- virtualDevices.add(device.getPublicVirtualDeviceObject());
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ VirtualDeviceImpl device = virtualDevicesSnapshot.get(i);
+ virtualDevices.add(device.getPublicVirtualDeviceObject());
}
return virtualDevices;
}
@@ -834,15 +841,17 @@ public class VirtualDeviceManagerService extends SystemService {
@Nullable
public LocaleList getPreferredLocaleListForUid(int uid) {
// TODO: b/263188984 support the case where an app is running on multiple VDs
+ VirtualDeviceImpl virtualDevice = null;
synchronized (mVirtualDeviceManagerLock) {
for (int i = 0; i < mAppsOnVirtualDevices.size(); i++) {
if (mAppsOnVirtualDevices.valueAt(i).contains(uid)) {
int deviceId = mAppsOnVirtualDevices.keyAt(i);
- return mVirtualDevices.get(deviceId).getDeviceLocaleList();
+ virtualDevice = mVirtualDevices.get(deviceId);
+ break;
}
}
}
- return null;
+ return virtualDevice == null ? null : virtualDevice.getDeviceLocaleList();
}
@Override
@@ -882,6 +891,11 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public VirtualDevice getVirtualDevice(int deviceId) {
+ return mImpl.getVirtualDevice(deviceId);
+ }
+
+ @Override
public long getDimDurationMillisForDeviceId(int deviceId) {
VirtualDeviceImpl virtualDevice = getVirtualDeviceForId(deviceId);
return virtualDevice == null ? -1 : virtualDevice.getDimDurationMillis();
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 89c9d690a82c..7eb7072520de 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -34,9 +34,10 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
-import android.app.admin.DevicePolicyManagerInternal;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.contextualsearch.CallbackToken;
@@ -65,11 +66,11 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.view.IWindowManager;
-import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import com.android.internal.R;
@@ -86,7 +87,6 @@ import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
public class ContextualSearchManagerService extends SystemService {
private static final String TAG = ContextualSearchManagerService.class.getSimpleName();
@@ -95,14 +95,25 @@ public class ContextualSearchManagerService extends SystemService {
private static final int MSG_INVALIDATE_TOKEN = 1;
private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes
+ /**
+ * Below are internal entrypoints not supported by the
+ * {@link ContextualSearchManager#startContextualSearch(int entrypoint)} method.
+ *
+ * <p>These values should be negative to avoid conflicting with the system entrypoints.
+ */
+
+ /** Entrypoint to be used when a foreground app invokes Contextual Search. */
+ private static final int INTERNAL_ENTRYPOINT_APP = -1;
+
private static final boolean DEBUG = false;
private final Context mContext;
+ private final ActivityManagerInternal mActivityManagerInternal;
private final ActivityTaskManagerInternal mAtmInternal;
private final PackageManagerInternal mPackageManager;
private final WindowManagerInternal mWmInternal;
- private final DevicePolicyManagerInternal mDpmInternal;
private final AudioManager mAudioManager;
+ private final UserManager mUserManager;
private final Object mLock = new Object();
private final AssistDataRequester mAssistDataRequester;
@@ -162,13 +173,15 @@ public class ContextualSearchManagerService extends SystemService {
super(context);
if (DEBUG) Log.d(TAG, "ContextualSearchManagerService created");
mContext = context;
+ mActivityManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(ActivityManagerInternal.class));
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
mAudioManager = context.getSystemService(AudioManager.class);
+ mUserManager = context.getSystemService(UserManager.class);
mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class));
- mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mAssistDataRequester = new AssistDataRequester(
mContext,
IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)),
@@ -295,6 +308,11 @@ public class ContextualSearchManagerService extends SystemService {
}
}
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS,
+ android.Manifest.permission.QUERY_USERS
+ })
private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
@@ -325,8 +343,7 @@ public class ContextualSearchManagerService extends SystemService {
visiblePackageNames.add(record.getComponentName().getPackageName());
activityTokens.add(record.getActivityToken());
}
- if (mDpmInternal != null
- && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+ if (mUserManager.isManagedProfile(record.getUserId())) {
isManagedProfileVisible = true;
}
}
@@ -348,7 +365,7 @@ public class ContextualSearchManagerService extends SystemService {
}
}
final ScreenshotHardwareBuffer shb = mWmInternal.takeContextualSearchScreenshot(
- (Flags.contextualSearchWindowLayer() ? csUid : -1));
+ (Flags.contextualSearchPreventSelfCapture() ? csUid : -1));
final Bitmap bm = shb != null ? shb.asBitmap() : null;
// Now that everything is fetched, putting it in the launchIntent.
if (bm != null) {
@@ -391,6 +408,20 @@ public class ContextualSearchManagerService extends SystemService {
}
}
+ private void enforceForegroundApp(@NonNull final String func) {
+ final int callingUid = Binder.getCallingUid();
+ final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid());
+ if (mActivityManagerInternal.getUidProcessState(callingUid)
+ > ActivityManager.PROCESS_STATE_TOP) {
+ // The calling process must be displaying an activity in foreground to
+ // trigger contextual search.
+ String msg = "Permission Denial: Cannot call " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + callingUid
+ + ", package=" + callingPackage + " without a foreground activity.";
+ throw new SecurityException(msg);
+ }
+ }
+
private void enforceOverridingPermission(@NonNull final String func) {
if (!(Binder.getCallingUid() == Process.SHELL_UID
|| Binder.getCallingUid() == Process.ROOT_UID
@@ -448,27 +479,44 @@ public class ContextualSearchManagerService extends SystemService {
}
@Override
+ public void startContextualSearchForForegroundApp() {
+ synchronized (this) {
+ if (DEBUG) {
+ Log.d(TAG, "Starting contextual search from: "
+ + mPackageManager.getNameForUid(Binder.getCallingUid()));
+ }
+ enforceForegroundApp("startContextualSearchForForegroundApp");
+ startContextualSearchInternal(INTERNAL_ENTRYPOINT_APP);
+ }
+ }
+
+ @Override
public void startContextualSearch(int entrypoint) {
synchronized (this) {
if (DEBUG) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
enforcePermission("startContextualSearch");
- final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+ startContextualSearchInternal(entrypoint);
+ }
+ }
- mAssistDataRequester.cancel();
- // Creates a new CallbackToken at mToken and an expiration handler.
- issueToken();
- // We get the launch intent with the system server's identity because the system
- // server has READ_FRAME_BUFFER permission to get the screenshot and because only
- // the system server can invoke non-exported activities.
- Binder.withCleanCallingIdentity(() -> {
- Intent launchIntent =
- getContextualSearchIntent(entrypoint, callingUserId, mToken);
- if (launchIntent != null) {
- int result = invokeContextualSearchIntent(launchIntent, callingUserId);
- if (DEBUG) Log.d(TAG, "Launch result: " + result);
+ private void startContextualSearchInternal(int entrypoint) {
+ final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+ mAssistDataRequester.cancel();
+ // Creates a new CallbackToken at mToken and an expiration handler.
+ issueToken();
+ // We get the launch intent with the system server's identity because the system
+ // server has READ_FRAME_BUFFER permission to get the screenshot and because only
+ // the system server can invoke non-exported activities.
+ Binder.withCleanCallingIdentity(() -> {
+ Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken);
+ if (launchIntent != null) {
+ int result = invokeContextualSearchIntent(launchIntent, callingUserId);
+ if (DEBUG) {
+ Log.d(TAG, "Launch intent: " + launchIntent);
+ Log.d(TAG, "Launch result: " + result);
}
- });
- }
+ }
+ });
}
@Override
@@ -501,7 +549,7 @@ public class ContextualSearchManagerService extends SystemService {
Binder.withCleanCallingIdentity(() -> {
final ScreenshotHardwareBuffer shb =
mWmInternal.takeContextualSearchScreenshot(
- (Flags.contextualSearchWindowLayer() ? callingUid : -1));
+ (Flags.contextualSearchPreventSelfCapture() ? callingUid : -1));
final Bitmap bm = shb != null ? shb.asBitmap() : null;
if (bm != null) {
bundle.putParcelable(ContextualSearchManager.EXTRA_SCREENSHOT,
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index 04edb5756599..cd632e638281 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -31,6 +31,8 @@ import static com.android.server.wm.WindowManagerInternal.OnWindowRemovedListene
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -81,6 +83,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
private PackageManagerInternal mPackageManagerInternal;
+ private RoleManager mRoleManager;
+
@Nullable
private WindowManagerInternal mWindowManager;
@@ -225,7 +229,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
}
@Override
- public void onStart() {}
+ public void onStart() {
+ }
@Override
public void onBootPhase(int phase) {
@@ -237,6 +242,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
init(getContext().getSystemService(MediaProjectionManager.class),
LocalServices.getService(WindowManagerInternal.class),
LocalServices.getService(PackageManagerInternal.class),
+ getContext().getSystemService(RoleManager.class),
getExemptedPackages()
);
if (sensitiveContentAppProtection()) {
@@ -247,7 +253,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
@VisibleForTesting
void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager,
- PackageManagerInternal packageManagerInternal, ArraySet<String> exemptedPackages) {
+ PackageManagerInternal packageManagerInternal, RoleManager roleManager,
+ ArraySet<String> exemptedPackages) {
if (DEBUG) Log.d(TAG, "init");
Objects.requireNonNull(projectionManager);
@@ -256,6 +263,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
mProjectionManager = projectionManager;
mWindowManager = windowManager;
mPackageManagerInternal = packageManagerInternal;
+ mRoleManager = roleManager;
mExemptedPackages = exemptedPackages;
// TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary
@@ -312,8 +320,10 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
boolean isPackageExempted = (mExemptedPackages != null && mExemptedPackages.contains(
projectionInfo.getPackageName()))
|| canRecordSensitiveContent(projectionInfo.getPackageName())
+ || holdsAppStreamingRole(projectionInfo.getPackageName(),
+ projectionInfo.getUserHandle())
|| isAutofillServiceRecorderPackage(projectionInfo.getUserHandle().getIdentifier(),
- projectionInfo.getPackageName());
+ projectionInfo.getPackageName());
// TODO(b/324447419): move GlobalSettings lookup to background thread
boolean isFeatureDisabled = Settings.Global.getInt(getContext().getContentResolver(),
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0;
@@ -348,6 +358,11 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
}
}
+ private boolean holdsAppStreamingRole(String packageName, UserHandle userHandle) {
+ return mRoleManager.getRoleHoldersAsUser(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING, userHandle).contains(packageName);
+ }
+
private void onProjectionEnd() {
synchronized (mSensitiveContentProtectionLock) {
mProjectionActive = false;
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 4976a63b016b..8eda17698b9b 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -18,7 +18,6 @@ package com.android.server;
import static android.app.Flags.enableCurrentModeTypeBinderCache;
import static android.app.Flags.enableNightModeBinderCache;
-import static android.app.Flags.modesApi;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.FORCE_INVERT_TYPE_DARK;
@@ -2208,14 +2207,12 @@ final class UiModeManagerService extends SystemService {
appliedOverrides = true;
}
- if (modesApi()) {
- // Computes final night mode values based on Attention Mode.
- mComputedNightMode = switch (mAttentionModeThemeOverlay) {
- case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT) -> true;
- case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY) -> false;
- default -> newComputedValue; // case OFF
- };
- }
+ // Computes final night mode values based on Attention Mode.
+ mComputedNightMode = switch (mAttentionModeThemeOverlay) {
+ case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT) -> true;
+ case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY) -> false;
+ default -> newComputedValue; // case OFF
+ };
if (appliedOverrides) {
return;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index cce29592d912..125824c3f37e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -618,7 +618,7 @@ public final class ActiveServices {
Slog.i(TAG, " Stopping fg for service " + r);
}
setServiceForegroundInnerLocked(r, 0, null, 0, 0,
- 0);
+ 0, /* systemRequestedTransition= */ true);
}
}
@@ -1839,7 +1839,7 @@ public final class ActiveServices {
ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType,
- callingUid);
+ callingUid, /* systemRequestedTransition= */ false);
}
} finally {
mAm.mInjector.restoreCallingIdentity(origId);
@@ -2155,7 +2155,7 @@ public final class ActiveServices {
@GuardedBy("mAm")
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
Notification notification, int flags, int foregroundServiceType,
- int callingUidIfStart) {
+ int callingUidIfStart, boolean systemRequestedTransition) {
if (id != 0) {
if (notification == null) {
throw new IllegalArgumentException("null notification");
@@ -2800,6 +2800,7 @@ public final class ActiveServices {
// earlier.
r.foregroundServiceType = 0;
r.mFgsNotificationWasDeferred = false;
+ r.systemRequestedFgToBg = systemRequestedTransition;
signalForegroundServiceObserversLocked(r);
resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
@@ -9339,14 +9340,22 @@ public final class ActiveServices {
if (sr.foregroundServiceType
== ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE
&& sr.foregroundId == notificationId) {
- if (DEBUG_FOREGROUND_SERVICE) {
- Slog.d(TAG, "Moving media service to foreground for package "
- + packageName);
+ // check if service is explicitly requested by app to not be in foreground.
+ if (sr.systemRequestedFgToBg) {
+ Slog.d(TAG,
+ "System initiated service transition to foreground "
+ + "for package "
+ + packageName);
+ setServiceForegroundInnerLocked(sr, sr.foregroundId,
+ sr.foregroundNoti, /* flags */ 0,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
+ /* callingUidStart */ 0, /* systemRequestedTransition */ true);
+ } else {
+ Slog.d(TAG,
+ "Ignoring system initiated foreground service transition for "
+ + "package"
+ + packageName);
}
- setServiceForegroundInnerLocked(sr, sr.foregroundId,
- sr.foregroundNoti, /* flags */ 0,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
- /* callingUidStart */ 0);
}
}
}
@@ -9379,13 +9388,14 @@ public final class ActiveServices {
if (sr.foregroundServiceType
== ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
&& sr.foregroundId == notificationId) {
- if (DEBUG_FOREGROUND_SERVICE) {
- Slog.d(TAG, "Forcing media foreground service to background for package "
- + packageName);
- }
+ Slog.d(TAG,
+ "System initiated transition of foreground service(type:media) to bg "
+ + "for package"
+ + packageName);
setServiceForegroundInnerLocked(sr, /* id */ 0,
/* notification */ null, /* flags */ 0,
- /* foregroundServiceType */ 0, /* callingUidStart */ 0);
+ /* foregroundServiceType */ 0, /* callingUidStart */ 0,
+ /* systemRequestedTransition */ true);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e3d7bd19b41..07a4d52f56ec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -301,6 +301,7 @@ import android.content.pm.ProviderInfoList;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SystemFeaturesCache;
import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
@@ -2559,9 +2560,20 @@ public class ActivityManagerService extends IActivityManager.Stub
mTraceErrorLogger = new TraceErrorLogger();
mComponentAliasResolver = new ComponentAliasResolver(this);
sCreatorTokenCacheCleaner = new Handler(mHandlerThread.getLooper());
+
+ ApplicationSharedMemory applicationSharedMemory = ApplicationSharedMemory.getInstance();
+ if (android.content.pm.Flags.cacheSdkSystemFeatures()) {
+ // Install the cache into the process-wide singleton for in-proc queries, as well as
+ // shared memory. Apps will inflate the cache from shared memory in bindApplication.
+ SystemFeaturesCache systemFeaturesCache =
+ new SystemFeaturesCache(SystemConfig.getInstance().getAvailableFeatures());
+ SystemFeaturesCache.setInstance(systemFeaturesCache);
+ applicationSharedMemory.writeSystemFeaturesCache(
+ systemFeaturesCache.getSdkFeatureVersions());
+ }
try {
mApplicationSharedMemoryReadOnlyFd =
- ApplicationSharedMemory.getInstance().getReadOnlyFileDescriptor();
+ applicationSharedMemory.getReadOnlyFileDescriptor();
} catch (IOException e) {
Slog.e(TAG, "Failed to get read only fd for shared memory", e);
throw new RuntimeException(e);
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 961022b7231b..517279bd7527 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -54,15 +54,21 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.os.Clock;
import com.android.internal.os.MonotonicClock;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.IoThread;
import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
import com.android.server.wm.WindowProcessController;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -1006,6 +1012,12 @@ public final class AppStartInfoTracker {
throws IOException, WireTypeMismatchException, ClassNotFoundException {
long token = proto.start(fieldId);
String pkgName = "";
+
+ // Create objects for reuse.
+ ByteArrayInputStream byteArrayInputStream = null;
+ ObjectInputStream objectInputStream = null;
+ TypedXmlPullParser typedXmlPullParser = null;
+
for (int next = proto.nextField();
next != ProtoInputStream.NO_MORE_FIELDS;
next = proto.nextField()) {
@@ -1017,7 +1029,7 @@ public final class AppStartInfoTracker {
AppStartInfoContainer container =
new AppStartInfoContainer(mAppStartInfoHistoryListSize);
int uid = container.readFromProto(proto, AppsStartInfoProto.Package.USERS,
- pkgName);
+ pkgName, byteArrayInputStream, objectInputStream, typedXmlPullParser);
// If the isolated process flag is enabled and the uid is that of an isolated
// process, then break early so that the container will not be added to mData.
@@ -1052,6 +1064,12 @@ public final class AppStartInfoTracker {
out = af.startWrite();
ProtoOutputStream proto = new ProtoOutputStream(out);
proto.write(AppsStartInfoProto.LAST_UPDATE_TIMESTAMP, now);
+
+ // Create objects for reuse.
+ ByteArrayOutputStream byteArrayOutputStream = null;
+ ObjectOutputStream objectOutputStream = null;
+ TypedXmlSerializer typedXmlSerializer = null;
+
synchronized (mLock) {
succeeded = forEachPackageLocked(
(packageName, records) -> {
@@ -1060,8 +1078,9 @@ public final class AppStartInfoTracker {
int uidArraySize = records.size();
for (int j = 0; j < uidArraySize; j++) {
try {
- records.valueAt(j)
- .writeToProto(proto, AppsStartInfoProto.Package.USERS);
+ records.valueAt(j).writeToProto(proto,
+ AppsStartInfoProto.Package.USERS, byteArrayOutputStream,
+ objectOutputStream, typedXmlSerializer);
} catch (IOException e) {
Slog.w(TAG, "Unable to write app start info into persistent"
+ "storage: " + e);
@@ -1414,19 +1433,23 @@ public final class AppStartInfoTracker {
}
@GuardedBy("mLock")
- void writeToProto(ProtoOutputStream proto, long fieldId) throws IOException {
+ void writeToProto(ProtoOutputStream proto, long fieldId,
+ ByteArrayOutputStream byteArrayOutputStream, ObjectOutputStream objectOutputStream,
+ TypedXmlSerializer typedXmlSerializer) throws IOException {
long token = proto.start(fieldId);
proto.write(AppsStartInfoProto.Package.User.UID, mUid);
int size = mInfos.size();
for (int i = 0; i < size; i++) {
- mInfos.get(i)
- .writeToProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO);
+ mInfos.get(i).writeToProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO,
+ byteArrayOutputStream, objectOutputStream, typedXmlSerializer);
}
proto.write(AppsStartInfoProto.Package.User.MONITORING_ENABLED, mMonitoringModeEnabled);
proto.end(token);
}
- int readFromProto(ProtoInputStream proto, long fieldId, String packageName)
+ int readFromProto(ProtoInputStream proto, long fieldId, String packageName,
+ ByteArrayInputStream byteArrayInputStream, ObjectInputStream objectInputStream,
+ TypedXmlPullParser typedXmlPullParser)
throws IOException, WireTypeMismatchException, ClassNotFoundException {
long token = proto.start(fieldId);
for (int next = proto.nextField();
@@ -1440,7 +1463,8 @@ public final class AppStartInfoTracker {
// Create record with monotonic time 0 in case the persisted record does not
// have a create time.
ApplicationStartInfo info = new ApplicationStartInfo(0);
- info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO);
+ info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO,
+ byteArrayInputStream, objectInputStream, typedXmlPullParser);
info.setPackageName(packageName);
mInfos.add(info);
break;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index b677297dfef2..4bfee1d8398f 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1464,7 +1464,10 @@ public class CachedAppOptimizer {
void onProcessFrozen(ProcessRecord frozenProc) {
if (useCompaction()) {
synchronized (mProcLock) {
- compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false);
+ // only full-compact if process is cached
+ if (frozenProc.mState.getSetAdj() >= mCompactThrottleMinOomAdj) {
+ compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false);
+ }
}
}
frozenProc.onProcessFrozen();
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index cc6fabc8fd67..4b6d6bc955cc 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -68,7 +68,7 @@ per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNE
per-file ActivityManager* = file:/ACTIVITY_SECURITY_OWNERS
# Aconfig Flags
-per-file flags.aconfig = yamasani@google.com, bills@google.com, nalini@google.com
+per-file flags.aconfig = yamasani@google.com, nalini@google.com
# Londoners
michaelwr@google.com #{LAST_RESORT_SUGGESTION}
@@ -77,4 +77,4 @@ narayan@google.com #{LAST_RESORT_SUGGESTION}
# Default
yamasani@google.com
hackbod@google.com #{LAST_RESORT_SUGGESTION}
-omakoto@google.com #{LAST_RESORT_SUGGESTION} \ No newline at end of file
+omakoto@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 61c5501a7b5a..13d367a95942 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -446,6 +446,8 @@ public class OomAdjuster {
private static final int CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD =
Flags.raiseBoundUiServiceThreshold() ? SERVICE_ADJ : PERCEPTIBLE_APP_ADJ;
+ static final long PERCEPTIBLE_TASK_TIMEOUT_MILLIS = 5 * 60 * 1000;
+
@VisibleForTesting
public static class Injector {
boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -1847,7 +1849,7 @@ public class OomAdjuster {
mHasVisibleActivities = false;
}
- void onOtherActivity() {
+ void onOtherActivity(long perceptibleTaskStoppedTimeMillis) {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
mAdjType = "cch-act";
@@ -1856,6 +1858,28 @@ public class OomAdjuster {
"Raise procstate to cached activity: " + app);
}
}
+ if (Flags.perceptibleTasks() && adj > PERCEPTIBLE_MEDIUM_APP_ADJ) {
+ if (perceptibleTaskStoppedTimeMillis >= 0) {
+ final long now = mInjector.getUptimeMillis();
+ if (now - perceptibleTaskStoppedTimeMillis < PERCEPTIBLE_TASK_TIMEOUT_MILLIS) {
+ adj = PERCEPTIBLE_MEDIUM_APP_ADJ;
+ mAdjType = "perceptible-act";
+ if (procState > PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ procState = PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
+
+ maybeSetProcessFollowUpUpdateLocked(app,
+ perceptibleTaskStoppedTimeMillis + PERCEPTIBLE_TASK_TIMEOUT_MILLIS,
+ now);
+ } else if (adj > PREVIOUS_APP_ADJ) {
+ adj = PREVIOUS_APP_ADJ;
+ mAdjType = "stale-perceptible-act";
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
+ }
+ }
+ }
+ }
mHasVisibleActivities = false;
}
}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index b0f808b39053..25175e6bee5f 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -1120,7 +1120,8 @@ final class ProcessStateRecord {
} else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
callback.onStoppingActivity((flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
} else {
- callback.onOtherActivity();
+ final long ts = mApp.getWindowProcessController().getPerceptibleTaskStoppedTimeMillis();
+ callback.onOtherActivity(ts);
}
mCachedAdj = callback.adj;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index ca34a13c55b1..f443e4455734 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -158,6 +158,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
boolean isForeground; // is service currently in foreground mode?
+ boolean systemRequestedFgToBg; // system requested service to transition to background.
boolean inSharedIsolatedProcess; // is the service in a shared isolated process
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 27c384a22fb6..c8fedf3d1765 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -293,6 +293,13 @@ flag {
}
flag {
+ name: "perceptible_tasks"
+ namespace: "system_performance"
+ description: "Boost the oom_score_adj of activities in perceptible tasks"
+ bug: "370890207"
+}
+
+flag {
name: "expedite_activity_launch_on_cold_start"
namespace: "system_performance"
description: "Notify ActivityTaskManager of cold starts early to fix app launch behavior."
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
index 30c2a82296ca..604cb30294a9 100644
--- a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -418,7 +418,9 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
evictedEvents.addAll(mCache);
mCache.clear();
}
- mSqliteWriteHandler.obtainMessage(WRITE_CACHE_EVICTED_OP_EVENTS, evictedEvents);
+ Message msg = mSqliteWriteHandler.obtainMessage(
+ WRITE_CACHE_EVICTED_OP_EVENTS, evictedEvents);
+ mSqliteWriteHandler.sendMessage(msg);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6d6e1fb6bfb3..ef80d59993e9 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -18,6 +18,7 @@ package com.android.server.audio;
import static android.media.audio.Flags.scoManagedByAudio;
import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
+import static com.android.media.audio.Flags.optimizeBtDeviceSwitch;
import static com.android.server.audio.AudioService.BT_COMM_DEVICE_ACTIVE_BLE_HEADSET;
import static com.android.server.audio.AudioService.BT_COMM_DEVICE_ACTIVE_BLE_SPEAKER;
import static com.android.server.audio.AudioService.BT_COMM_DEVICE_ACTIVE_SCO;
@@ -290,8 +291,8 @@ public class AudioDeviceBroker {
}
@GuardedBy("mDeviceStateLock")
- /*package*/ void onSetBtScoActiveDevice(BluetoothDevice btDevice) {
- mBtHelper.onSetBtScoActiveDevice(btDevice);
+ /*package*/ void onSetBtScoActiveDevice(BluetoothDevice btDevice, boolean deviceSwitch) {
+ mBtHelper.onSetBtScoActiveDevice(btDevice, deviceSwitch);
}
/*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
@@ -941,6 +942,7 @@ public class AudioDeviceBroker {
final @NonNull String mEventSource;
final int mAudioSystemDevice;
final int mMusicDevice;
+ final boolean mIsDeviceSwitch;
BtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, int state,
int audioDevice, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) {
@@ -953,6 +955,8 @@ public class AudioDeviceBroker {
mEventSource = d.mEventSource;
mAudioSystemDevice = audioDevice;
mMusicDevice = AudioSystem.DEVICE_NONE;
+ mIsDeviceSwitch = optimizeBtDeviceSwitch()
+ && d.mNewDevice != null && d.mPreviousDevice != null;
}
// constructor used by AudioDeviceBroker to search similar message
@@ -966,6 +970,7 @@ public class AudioDeviceBroker {
mSupprNoisy = false;
mVolume = -1;
mIsLeOutput = false;
+ mIsDeviceSwitch = false;
}
// constructor used by AudioDeviceInventory when config change failed
@@ -980,6 +985,7 @@ public class AudioDeviceBroker {
mSupprNoisy = false;
mVolume = -1;
mIsLeOutput = false;
+ mIsDeviceSwitch = false;
}
BtDeviceInfo(@NonNull BtDeviceInfo src, int state) {
@@ -992,6 +998,7 @@ public class AudioDeviceBroker {
mEventSource = src.mEventSource;
mAudioSystemDevice = src.mAudioSystemDevice;
mMusicDevice = src.mMusicDevice;
+ mIsDeviceSwitch = false;
}
// redefine equality op so we can match messages intended for this device
@@ -1026,7 +1033,8 @@ public class AudioDeviceBroker {
+ " isLeOutput=" + mIsLeOutput
+ " eventSource=" + mEventSource
+ " audioSystemDevice=" + mAudioSystemDevice
- + " musicDevice=" + mMusicDevice;
+ + " musicDevice=" + mMusicDevice
+ + " isDeviceSwitch=" + mIsDeviceSwitch;
}
}
@@ -1196,6 +1204,8 @@ public class AudioDeviceBroker {
AudioSystem.setParameters("A2dpSuspended=true");
AudioSystem.setParameters("LeAudioSuspended=true");
AudioSystem.setParameters("BT_SCO=on");
+ mBluetoothA2dpSuspendedApplied = true;
+ mBluetoothLeSuspendedApplied = true;
} else {
AudioSystem.setParameters("BT_SCO=off");
if (mBluetoothA2dpSuspendedApplied) {
@@ -1680,10 +1690,11 @@ public class AudioDeviceBroker {
}
/*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes,
- boolean connect, @Nullable BluetoothDevice btDevice) {
+ boolean connect, @Nullable BluetoothDevice btDevice,
+ boolean deviceSwitch) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleDeviceConnection(
- attributes, connect, false /*for test*/, btDevice);
+ attributes, connect, false /*for test*/, btDevice, deviceSwitch);
}
}
@@ -1776,6 +1787,18 @@ public class AudioDeviceBroker {
pw.println("\n" + prefix + "mScoManagedByAudio: " + mScoManagedByAudio);
+ pw.println("\n" + prefix + "Bluetooth SCO on"
+ + ", requested: " + mBluetoothScoOn
+ + ", applied: " + mBluetoothScoOnApplied);
+ pw.println("\n" + prefix + "Bluetooth A2DP suspended"
+ + ", requested ext: " + mBluetoothA2dpSuspendedExt
+ + ", requested int: " + mBluetoothA2dpSuspendedInt
+ + ", applied " + mBluetoothA2dpSuspendedApplied);
+ pw.println("\n" + prefix + "Bluetooth LE Audio suspended"
+ + ", requested ext: " + mBluetoothLeSuspendedExt
+ + ", requested int: " + mBluetoothLeSuspendedInt
+ + ", applied " + mBluetoothLeSuspendedApplied);
+
mBtHelper.dump(pw, prefix);
}
@@ -1930,10 +1953,12 @@ public class AudioDeviceBroker {
|| btInfo.mIsLeOutput)
? mAudioService.getBluetoothContextualVolumeStream()
: AudioSystem.STREAM_DEFAULT);
- if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
+ if ((btInfo.mProfile == BluetoothProfile.LE_AUDIO
|| btInfo.mProfile == BluetoothProfile.HEARING_AID
|| (mScoManagedByAudio
- && btInfo.mProfile == BluetoothProfile.HEADSET)) {
+ && btInfo.mProfile == BluetoothProfile.HEADSET))
+ && (btInfo.mState == BluetoothProfile.STATE_CONNECTED
+ || !btInfo.mIsDeviceSwitch)) {
onUpdateCommunicationRouteClient(
bluetoothScoRequestOwnerAttributionSource(),
"setBluetoothActiveDevice");
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ef10793fd955..ae91934e7498 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -799,7 +799,7 @@ public class AudioDeviceInventory {
di.mDeviceAddress,
di.mDeviceName),
AudioSystem.DEVICE_STATE_AVAILABLE,
- di.mDeviceCodecFormat);
+ di.mDeviceCodecFormat, false /*deviceSwitch*/);
if (asDeviceConnectionFailure() && res != AudioSystem.AUDIO_STATUS_OK) {
failedReconnectionDeviceList.add(di);
}
@@ -811,7 +811,7 @@ public class AudioDeviceInventory {
EventLogger.Event.ALOGE, TAG);
mConnectedDevices.remove(di.getKey(), di);
if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
- mDeviceBroker.onSetBtScoActiveDevice(null);
+ mDeviceBroker.onSetBtScoActiveDevice(null, false /*deviceSwitch*/);
}
}
}
@@ -851,7 +851,8 @@ public class AudioDeviceInventory {
Log.d(TAG, "onSetBtActiveDevice"
+ " btDevice=" + btInfo.mDevice
+ " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile)
- + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState));
+ + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState)
+ + " isDeviceSwitch=" + btInfo.mIsDeviceSwitch);
}
String address = btInfo.mDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
@@ -897,7 +898,8 @@ public class AudioDeviceInventory {
break;
case BluetoothProfile.A2DP:
if (switchToUnavailable) {
- makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
+ makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat,
+ btInfo.mIsDeviceSwitch);
} else if (switchToAvailable) {
// device is not already connected
if (btInfo.mVolume != -1) {
@@ -911,7 +913,7 @@ public class AudioDeviceInventory {
break;
case BluetoothProfile.HEARING_AID:
if (switchToUnavailable) {
- makeHearingAidDeviceUnavailable(address);
+ makeHearingAidDeviceUnavailable(address, btInfo.mIsDeviceSwitch);
} else if (switchToAvailable) {
makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
streamType, "onSetBtActiveDevice");
@@ -921,7 +923,8 @@ public class AudioDeviceInventory {
case BluetoothProfile.LE_AUDIO_BROADCAST:
if (switchToUnavailable) {
makeLeAudioDeviceUnavailableNow(address,
- btInfo.mAudioSystemDevice, di.mDeviceCodecFormat);
+ btInfo.mAudioSystemDevice, di.mDeviceCodecFormat,
+ btInfo.mIsDeviceSwitch);
} else if (switchToAvailable) {
makeLeAudioDeviceAvailable(
btInfo, streamType, codec, "onSetBtActiveDevice");
@@ -930,9 +933,10 @@ public class AudioDeviceInventory {
case BluetoothProfile.HEADSET:
if (mDeviceBroker.isScoManagedByAudio()) {
if (switchToUnavailable) {
- mDeviceBroker.onSetBtScoActiveDevice(null);
+ mDeviceBroker.onSetBtScoActiveDevice(null, btInfo.mIsDeviceSwitch);
} else if (switchToAvailable) {
- mDeviceBroker.onSetBtScoActiveDevice(btInfo.mDevice);
+ mDeviceBroker.onSetBtScoActiveDevice(
+ btInfo.mDevice, false /*deviceSwitch*/);
}
}
break;
@@ -1053,19 +1057,19 @@ public class AudioDeviceInventory {
/*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
synchronized (mDevicesLock) {
- makeA2dpDeviceUnavailableNow(address, a2dpCodec);
+ makeA2dpDeviceUnavailableNow(address, a2dpCodec, false /*deviceSwitch*/);
}
}
/*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device, int codec) {
synchronized (mDevicesLock) {
- makeLeAudioDeviceUnavailableNow(address, device, codec);
+ makeLeAudioDeviceUnavailableNow(address, device, codec, false /*deviceSwitch*/);
}
}
/*package*/ void onMakeHearingAidDeviceUnavailableNow(String address) {
synchronized (mDevicesLock) {
- makeHearingAidDeviceUnavailable(address);
+ makeHearingAidDeviceUnavailable(address, false /*deviceSwitch*/);
}
}
@@ -1180,7 +1184,8 @@ public class AudioDeviceInventory {
}
if (!handleDeviceConnection(wdcs.mAttributes,
- wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) {
+ wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest,
+ null, false /*deviceSwitch*/)) {
// change of connection state failed, bailout
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
.record();
@@ -1788,14 +1793,15 @@ public class AudioDeviceInventory {
*/
/*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes,
boolean connect, boolean isForTesting,
- @Nullable BluetoothDevice btDevice) {
+ @Nullable BluetoothDevice btDevice,
+ boolean deviceSwitch) {
int device = attributes.getInternalType();
String address = attributes.getAddress();
String deviceName = attributes.getName();
if (AudioService.DEBUG_DEVICES) {
Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:"
+ Integer.toHexString(device) + " address:" + address
- + " name:" + deviceName + ")");
+ + " name:" + deviceName + ", deviceSwitch: " + deviceSwitch + ")");
}
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "handleDeviceConnection")
.set(MediaMetrics.Property.ADDRESS, address)
@@ -1829,7 +1835,8 @@ public class AudioDeviceInventory {
res = AudioSystem.AUDIO_STATUS_OK;
} else {
res = mAudioSystem.setDeviceConnectionState(attributes,
- AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT,
+ false /*deviceSwitch*/);
}
if (res != AudioSystem.AUDIO_STATUS_OK) {
final String reason = "not connecting device 0x" + Integer.toHexString(device)
@@ -1856,7 +1863,8 @@ public class AudioDeviceInventory {
status = true;
} else if (!connect && isConnected) {
mAudioSystem.setDeviceConnectionState(attributes,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT,
+ deviceSwitch);
// always remove even if disconnection failed
mConnectedDevices.remove(deviceKey);
mDeviceBroker.postCheckCommunicationDeviceRemoval(attributes);
@@ -2030,7 +2038,7 @@ public class AudioDeviceInventory {
}
}
if (disconnect) {
- mDeviceBroker.onSetBtScoActiveDevice(null);
+ mDeviceBroker.onSetBtScoActiveDevice(null, false /*deviceSwitch*/);
}
}
@@ -2068,7 +2076,8 @@ public class AudioDeviceInventory {
|| info.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST)
&& info.mIsLeOutput)
|| info.mProfile == BluetoothProfile.HEARING_AID
- || info.mProfile == BluetoothProfile.A2DP)) {
+ || info.mProfile == BluetoothProfile.A2DP)
+ && !info.mIsDeviceSwitch) {
@AudioService.ConnectionState int asState =
(info.mState == BluetoothProfile.STATE_CONNECTED)
? AudioService.CONNECTION_STATE_CONNECTED
@@ -2124,7 +2133,7 @@ public class AudioDeviceInventory {
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_AVAILABLE, codec);
+ AudioSystem.DEVICE_STATE_AVAILABLE, codec, false);
// TODO: log in MediaMetrics once distinction between connection failure and
// double connection is made.
@@ -2362,7 +2371,7 @@ public class AudioDeviceInventory {
}
@GuardedBy("mDevicesLock")
- private void makeA2dpDeviceUnavailableNow(String address, int codec) {
+ private void makeA2dpDeviceUnavailableNow(String address, int codec, boolean deviceSwitch) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
.set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow");
@@ -2393,7 +2402,7 @@ public class AudioDeviceInventory {
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, codec);
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, codec, deviceSwitch);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
@@ -2404,7 +2413,8 @@ public class AudioDeviceInventory {
} else {
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable")).printSlog(EventLogger.Event.ALOGI, TAG));
+ + " made unavailable, deviceSwitch" + deviceSwitch))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -2440,7 +2450,7 @@ public class AudioDeviceInventory {
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
AudioSystem.DEVICE_STATE_AVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.AUDIO_FORMAT_DEFAULT, false);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make available A2DP source device addr="
@@ -2465,7 +2475,7 @@ public class AudioDeviceInventory {
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.AUDIO_FORMAT_DEFAULT, false);
// always remove regardless of the result
mConnectedDevices.remove(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
@@ -2485,7 +2495,7 @@ public class AudioDeviceInventory {
DEVICE_OUT_HEARING_AID, address, name);
final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.AUDIO_FORMAT_DEFAULT, false);
if (asDeviceConnectionFailure() && res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueueAndSlog(
"APM failed to make available HearingAid addr=" + address
@@ -2515,12 +2525,12 @@ public class AudioDeviceInventory {
}
@GuardedBy("mDevicesLock")
- private void makeHearingAidDeviceUnavailable(String address) {
+ private void makeHearingAidDeviceUnavailable(String address, boolean deviceSwitch) {
AudioDeviceAttributes ada = new AudioDeviceAttributes(
DEVICE_OUT_HEARING_AID, address);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.AUDIO_FORMAT_DEFAULT, deviceSwitch);
// always remove regardless of return code
mConnectedDevices.remove(
DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address));
@@ -2622,7 +2632,7 @@ public class AudioDeviceInventory {
AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_AVAILABLE, codec);
+ AudioSystem.DEVICE_STATE_AVAILABLE, codec, false /*deviceSwitch*/);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueueAndSlog(
"APM failed to make available LE Audio device addr=" + address
@@ -2669,13 +2679,13 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeLeAudioDeviceUnavailableNow(String address, int device,
- @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) {
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, boolean deviceSwitch) {
AudioDeviceAttributes ada = null;
if (device != AudioSystem.DEVICE_NONE) {
ada = new AudioDeviceAttributes(device, address);
final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
- codec);
+ codec, deviceSwitch);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
@@ -2685,7 +2695,8 @@ public class AudioDeviceInventory {
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable").printSlog(EventLogger.Event.ALOGI, TAG));
+ + " made unavailable, deviceSwitch" + deviceSwitch)
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 86871ea45d13..d800503c658e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -63,6 +63,7 @@ import static com.android.media.audio.Flags.audioserverPermissions;
import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
import static com.android.media.audio.Flags.deferWearPermissionUpdates;
import static com.android.media.audio.Flags.equalScoLeaVcIndexRange;
+import static com.android.media.audio.Flags.optimizeBtDeviceSwitch;
import static com.android.media.audio.Flags.replaceStreamBtSco;
import static com.android.media.audio.Flags.ringMyCar;
import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
@@ -585,6 +586,9 @@ public class AudioService extends IAudioService.Stub
// protects mRingerMode
private final Object mSettingsLock = new Object();
+ // protects VolumeStreamState / VolumeGroupState operations
+ private final Object mVolumeStateLock = new Object();
+
/** Maximum volume index values for audio streams */
protected static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
@@ -1611,7 +1615,7 @@ public class AudioService extends IAudioService.Stub
private void initVolumeStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final VolumeStreamState streamState = getVssForStream(streamType);
if (streamState == null) {
@@ -2419,7 +2423,7 @@ public class AudioService extends IAudioService.Stub
private void checkAllAliasStreamVolumes() {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
int streamAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1);
@@ -2501,7 +2505,7 @@ public class AudioService extends IAudioService.Stub
private void onUpdateVolumeStatesForAudioDevice(int device, String caller) {
final int numStreamTypes = AudioSystem.getNumStreamTypes();
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
updateVolumeStates(device, streamType, caller);
}
@@ -2770,7 +2774,7 @@ public class AudioService extends IAudioService.Stub
updateDefaultVolumes();
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
getVssForStreamOrDefault(AudioSystem.STREAM_DTMF)
.setAllIndexes(getVssForStreamOrDefault(dtmfStreamAlias), caller);
getVssForStreamOrDefault(AudioSystem.STREAM_ACCESSIBILITY).setSettingName(
@@ -3232,8 +3236,10 @@ public class AudioService extends IAudioService.Stub
// Each stream will read its own persisted settings
// Broadcast the sticky intents
- broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, mRingerModeExternal);
- broadcastRingerMode(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, mRingerMode);
+ synchronized (mSettingsLock) {
+ broadcastRingerMode(AudioManager.RINGER_MODE_CHANGED_ACTION, mRingerModeExternal);
+ broadcastRingerMode(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION, mRingerMode);
+ }
// Broadcast vibrate settings
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
@@ -4235,7 +4241,7 @@ public class AudioService extends IAudioService.Stub
private void muteAliasStreams(int streamAlias, boolean state) {
// Locking mSettingsLock to avoid inversion when calling doMute -> updateVolumeGroupIndex
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
List<Integer> streamsToMute = new ArrayList<>();
for (int streamIdx = 0; streamIdx < mStreamStates.size(); streamIdx++) {
final VolumeStreamState vss = mStreamStates.valueAt(streamIdx);
@@ -4279,7 +4285,7 @@ public class AudioService extends IAudioService.Stub
// Locking mSettingsLock to avoid inversion when calling vss.mute -> vss.doMute ->
// vss.updateVolumeGroupIndex
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final VolumeStreamState streamState = getVssForStreamOrDefault(streamAlias);
// if unmuting causes a change, it was muted
wasMuted = streamState.mute(false, "onUnmuteStreamOnSingleVolDevice");
@@ -4455,7 +4461,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getVolumeGroupVolumeIndex(int) */
public int getVolumeGroupVolumeIndex(int groupId) {
super.getVolumeGroupVolumeIndex_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, "No volume group for id " + groupId);
return 0;
@@ -4472,7 +4478,7 @@ public class AudioService extends IAudioService.Stub
MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
public int getVolumeGroupMaxVolumeIndex(int groupId) {
super.getVolumeGroupMaxVolumeIndex_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, "No volume group for id " + groupId);
return 0;
@@ -4487,7 +4493,7 @@ public class AudioService extends IAudioService.Stub
MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
public int getVolumeGroupMinVolumeIndex(int groupId) {
super.getVolumeGroupMinVolumeIndex_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, "No volume group for id " + groupId);
return 0;
@@ -4632,7 +4638,7 @@ public class AudioService extends IAudioService.Stub
@android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
public int getLastAudibleVolumeForVolumeGroup(int groupId) {
super.getLastAudibleVolumeForVolumeGroup_enforcePermission();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, ": no volume group found for id " + groupId);
return 0;
@@ -4644,7 +4650,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#isVolumeGroupMuted(int) */
public boolean isVolumeGroupMuted(int groupId) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
Log.e(TAG, ": no volume group found for id " + groupId);
return false;
@@ -4990,6 +4996,8 @@ public class AudioService extends IAudioService.Stub
+ cacheGetStreamMinMaxVolume());
pw.println("\tandroid.media.audio.Flags.cacheGetStreamVolume:"
+ cacheGetStreamVolume());
+ pw.println("\tcom.android.media.audio.optimizeBtDeviceSwitch:"
+ + optimizeBtDeviceSwitch());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -5470,7 +5478,7 @@ public class AudioService extends IAudioService.Stub
}
streamType = replaceBtScoStreamWithVoiceCall(streamType, "isStreamMute");
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
ensureValidStreamType(streamType);
return getVssForStreamOrDefault(streamType).mIsMuted;
}
@@ -5651,7 +5659,7 @@ public class AudioService extends IAudioService.Stub
}
private int getStreamVolume(int streamType, int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
int index = vss.getIndex(device);
@@ -5695,7 +5703,7 @@ public class AudioService extends IAudioService.Stub
vib.setMinVolumeIndex((vss.mIndexMin + 5) / 10);
vib.setMaxVolumeIndex((vss.mIndexMax + 5) / 10);
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final int index;
if (isFixedVolumeDevice(ada.getInternalType())) {
index = (vss.mIndexMax + 5) / 10;
@@ -6263,7 +6271,7 @@ public class AudioService extends IAudioService.Stub
// ring and notifications volume should never be 0 when not silenced
if (sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_RING
|| sStreamVolumeAlias.get(streamType) == AudioSystem.STREAM_NOTIFICATION) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int i = 0; i < vss.mIndexMap.size(); i++) {
int device = vss.mIndexMap.keyAt(i);
int value = vss.mIndexMap.valueAt(i);
@@ -7023,7 +7031,7 @@ public class AudioService extends IAudioService.Stub
}
streamState.readSettings();
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// unmute stream that was muted but is not affect by mute anymore
if (streamState.mIsMuted && ((!isStreamAffectedByMute(streamType) &&
!isStreamMutedByRingerOrZenMode(streamType)) || mUseFixedVolume)) {
@@ -8056,14 +8064,14 @@ public class AudioService extends IAudioService.Stub
public Set<Integer> getDeviceSetForStream(int stream) {
stream = replaceBtScoStreamWithVoiceCall(stream, "getDeviceSetForStream");
ensureValidStreamType(stream);
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
return getVssForStreamOrDefault(stream).observeDevicesForStream_syncVSS(true);
}
}
private void onObserveDevicesForAllStreams(int skipStream) {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int stream = 0; stream < mStreamStates.size(); stream++) {
final VolumeStreamState vss = mStreamStates.valueAt(stream);
if (vss != null && vss.getStreamType() != skipStream) {
@@ -8645,7 +8653,7 @@ public class AudioService extends IAudioService.Stub
private void readVolumeGroupsSettings(boolean userSwitch) {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (DEBUG_VOL) {
Log.d(TAG, "readVolumeGroupsSettings userSwitch=" + userSwitch);
}
@@ -8703,7 +8711,7 @@ public class AudioService extends IAudioService.Stub
// NOTE: Locking order for synchronized objects related to volume management:
// 1 mSettingsLock
- // 2 VolumeStreamState.class
+ // 2 mVolumeStateLock
private class VolumeGroupState {
private final AudioVolumeGroup mAudioVolumeGroup;
private final SparseIntArray mIndexMap = new SparseIntArray(8);
@@ -8797,7 +8805,7 @@ public class AudioService extends IAudioService.Stub
* Mute/unmute the volume group
* @param muted the new mute state
*/
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
public boolean mute(boolean muted) {
if (!isMutable()) {
// Non mutable volume group
@@ -8821,7 +8829,7 @@ public class AudioService extends IAudioService.Stub
public void adjustVolume(int direction, int flags) {
synchronized (mSettingsLock) {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int device = getDeviceForVolume();
int previousIndex = getIndex(device);
if (isMuteAdjust(direction) && !isMutable()) {
@@ -8875,14 +8883,14 @@ public class AudioService extends IAudioService.Stub
}
public int getVolumeIndex() {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
return getIndex(getDeviceForVolume());
}
}
public void setVolumeIndex(int index, int flags) {
synchronized (mSettingsLock) {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (mUseFixedVolume) {
return;
}
@@ -8891,7 +8899,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private void setVolumeIndex(int index, int device, int flags) {
// Update cache & persist (muted by volume 0 shall be persisted)
updateVolumeIndex(index, device);
@@ -8904,7 +8912,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
public void updateVolumeIndex(int index, int device) {
// Filter persistency if already exist and the index has not changed
if (mIndexMap.indexOfKey(device) < 0 || mIndexMap.get(device) != index) {
@@ -8922,7 +8930,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private void setVolumeIndexInt(int index, int device, int flags) {
// Reflect mute state of corresponding stream by forcing index to 0 if muted
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
@@ -8955,14 +8963,14 @@ public class AudioService extends IAudioService.Stub
mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, muted, device);
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private int getIndex(int device) {
int index = mIndexMap.get(device, -1);
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private boolean hasIndexForDevice(int device) {
return (mIndexMap.get(device, -1) != -1);
}
@@ -8989,7 +8997,7 @@ public class AudioService extends IAudioService.Stub
public void applyAllVolumes(boolean userSwitch) {
String caller = "from vgs";
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// apply device specific volumes first
for (int i = 0; i < mIndexMap.size(); i++) {
int device = mIndexMap.keyAt(i);
@@ -9092,25 +9100,27 @@ public class AudioService extends IAudioService.Stub
if (mUseFixedVolume || mHasValidStreamType) {
return;
}
- if (DEBUG_VOL) {
- Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
- + mAudioVolumeGroup.name()
- + ", device " + AudioSystem.getOutputDeviceName(device)
- + " and User=" + getCurrentUserId()
- + " mSettingName: " + mSettingName);
- }
+ synchronized (mVolumeStateLock) {
+ if (DEBUG_VOL) {
+ Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device)
+ + " for group " + mAudioVolumeGroup.name()
+ + ", device " + AudioSystem.getOutputDeviceName(device)
+ + " and User=" + getCurrentUserId()
+ + " mSettingName: " + mSettingName);
+ }
- boolean success = mSettings.putSystemIntForUser(mContentResolver,
- getSettingNameForDevice(device),
- getIndex(device),
- getVolumePersistenceUserId());
- if (!success) {
- Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name());
+ boolean success = mSettings.putSystemIntForUser(mContentResolver,
+ getSettingNameForDevice(device),
+ getIndex(device),
+ getVolumePersistenceUserId());
+ if (!success) {
+ Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name());
+ }
}
}
public void readSettings() {
- synchronized (AudioService.VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// force maximum volume on all streams if fixed volume property is set
if (mUseFixedVolume) {
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -9144,7 +9154,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @GuardedBy("AudioService.VolumeStreamState.class")
+ @GuardedBy("AudioService.this.mVolumeStateLock")
private int getValidIndex(int index) {
if (index < mIndexMin) {
return mIndexMin;
@@ -9218,7 +9228,7 @@ public class AudioService extends IAudioService.Stub
// 1 mScoclient OR mSafeMediaVolumeState
// 2 mSetModeLock
// 3 mSettingsLock
- // 4 VolumeStreamState.class
+ // 4 mVolumeStateLock
/*package*/ class VolumeStreamState {
private final int mStreamType;
private VolumeGroupState mVolumeGroupState = null;
@@ -9427,7 +9437,7 @@ public class AudioService extends IAudioService.Stub
*
* This is a reference to the local list, do not modify.
*/
- @GuardedBy("VolumeStreamState.class")
+ @GuardedBy("mVolumeStateLock")
@NonNull
public Set<Integer> observeDevicesForStream_syncVSS(
boolean checkOthers) {
@@ -9495,7 +9505,7 @@ public class AudioService extends IAudioService.Stub
public void readSettings() {
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// force maximum volume on all streams if fixed volume property is set
if (mUseFixedVolume) {
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -9515,7 +9525,7 @@ public class AudioService extends IAudioService.Stub
}
}
}
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
// retrieve current volume for device
@@ -9548,7 +9558,7 @@ public class AudioService extends IAudioService.Stub
* will send the non-zero index together with muted state. Otherwise, index 0 will be sent
* to native for signalising a muted stream.
**/
- @GuardedBy("VolumeStreamState.class")
+ @GuardedBy("mVolumeStateLock")
private void setStreamVolumeIndex(int index, int device) {
// Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
// This allows RX path muting by the audio HAL only when explicitly muted but not when
@@ -9570,8 +9580,8 @@ public class AudioService extends IAudioService.Stub
mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, muted, device);
}
- // must be called while synchronized VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
+ // must be called while synchronized mVolumeStateLock
+ @GuardedBy("mVolumeStateLock")
/*package*/ void applyDeviceVolume_syncVSS(int device) {
int index;
if (isFullyMuted() && !ringMyCar()) {
@@ -9597,7 +9607,7 @@ public class AudioService extends IAudioService.Stub
}
public void applyAllVolumes() {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// apply device specific volumes first
int index;
boolean isAbsoluteVolume = false;
@@ -9659,7 +9669,7 @@ public class AudioService extends IAudioService.Stub
final boolean isCurrentDevice;
final StringBuilder aliasStreamIndexes = new StringBuilder();
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
oldIndex = getIndex(device);
index = getValidIndex(index, hasModifyAudioSettings);
// for STREAM_SYSTEM_ENFORCED, do not sync aliased streams on the enforced index
@@ -9708,76 +9718,82 @@ public class AudioService extends IAudioService.Stub
}
}
}
- }
- }
- if (changed) {
- // If associated to volume group, update group cache
- updateVolumeGroupIndex(device, /* forceMuteState= */ false);
-
- oldIndex = (oldIndex + 5) / 10;
- index = (index + 5) / 10;
- // log base stream changes to the event log
- if (sStreamVolumeAlias.get(mStreamType, /*valueIfKeyNotFound=*/-1) == mStreamType) {
- if (caller == null) {
- Log.w(TAG, "No caller for volume_changed event", new Throwable());
- }
- EventLogTags.writeVolumeChanged(mStreamType, oldIndex, index, mIndexMax / 10,
- caller);
- }
- // fire changed intents for all streams, but only when the device it changed on
- // is the current device
- if ((index != oldIndex) && isCurrentDevice) {
- // for single volume devices, only send the volume change broadcast
- // on the alias stream
- final int streamAlias = sStreamVolumeAlias.get(
- mStreamType, /*valueIfKeyNotFound=*/-1);
- if (!mIsSingleVolume || streamAlias == mStreamType) {
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
- mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE,
- oldIndex);
- int extraStreamType = mStreamType;
- // TODO: remove this when deprecating STREAM_BLUETOOTH_SCO
- if (isStreamBluetoothSco(mStreamType)) {
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- AudioSystem.STREAM_BLUETOOTH_SCO);
- extraStreamType = AudioSystem.STREAM_BLUETOOTH_SCO;
- } else {
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- mStreamType);
- }
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
- streamAlias);
-
- if (mStreamType == streamAlias) {
- String aliasStreamIndexesString = "";
- if (!aliasStreamIndexes.isEmpty()) {
- aliasStreamIndexesString =
- " aliased streams: " + aliasStreamIndexes;
- }
- AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
- extraStreamType, aliasStreamIndexesString, index, oldIndex));
- if (extraStreamType != mStreamType) {
- AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
- mStreamType, aliasStreamIndexesString, index, oldIndex));
+ if (changed) {
+ // If associated to volume group, update group cache
+ updateVolumeGroupIndex(device, /* forceMuteState= */ false);
+
+ oldIndex = (oldIndex + 5) / 10;
+ index = (index + 5) / 10;
+ // log base stream changes to the event log
+ if (sStreamVolumeAlias.get(mStreamType, /*valueIfKeyNotFound=*/-1)
+ == mStreamType) {
+ if (caller == null) {
+ Log.w(TAG, "No caller for volume_changed event", new Throwable());
}
+ EventLogTags.writeVolumeChanged(
+ mStreamType, oldIndex, index, mIndexMax / 10, caller);
}
- sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
- if (extraStreamType != mStreamType) {
- // send multiple intents in case we merged voice call and bt sco streams
- mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
- mStreamType);
- // do not use the options in thid case which could discard
- // the previous intent
- sendBroadcastToAll(mVolumeChanged, null);
+ // fire changed intents for all streams, but only when the device it changed
+ // on
+ // is the current device
+ if ((index != oldIndex) && isCurrentDevice) {
+ // for single volume devices, only send the volume change broadcast
+ // on the alias stream
+ final int streamAlias =
+ sStreamVolumeAlias.get(mStreamType, /*valueIfKeyNotFound=*/-1);
+ if (!mIsSingleVolume || streamAlias == mStreamType) {
+ mVolumeChanged.putExtra(
+ AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
+ mVolumeChanged.putExtra(
+ AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
+ int extraStreamType = mStreamType;
+ // TODO: remove this when deprecating STREAM_BLUETOOTH_SCO
+ if (isStreamBluetoothSco(mStreamType)) {
+ mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+ AudioSystem.STREAM_BLUETOOTH_SCO);
+ extraStreamType = AudioSystem.STREAM_BLUETOOTH_SCO;
+ } else {
+ mVolumeChanged.putExtra(
+ AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+ }
+ mVolumeChanged.putExtra(
+ AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS, streamAlias);
+
+ if (mStreamType == streamAlias) {
+ String aliasStreamIndexesString = "";
+ if (!aliasStreamIndexes.isEmpty()) {
+ aliasStreamIndexesString =
+ " aliased streams: " + aliasStreamIndexes;
+ }
+ AudioService.sVolumeLogger.enqueue(
+ new VolChangedBroadcastEvent(extraStreamType,
+ aliasStreamIndexesString, index, oldIndex));
+ if (extraStreamType != mStreamType) {
+ AudioService.sVolumeLogger.enqueue(
+ new VolChangedBroadcastEvent(mStreamType,
+ aliasStreamIndexesString, index, oldIndex));
+ }
+ }
+ sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
+ if (extraStreamType != mStreamType) {
+ // send multiple intents in case we merged voice call and bt sco
+ // streams
+ mVolumeChanged.putExtra(
+ AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+ // do not use the options in thid case which could discard
+ // the previous intent
+ sendBroadcastToAll(mVolumeChanged, null);
+ }
+ }
}
}
+ return changed;
}
}
- return changed;
}
public int getIndex(int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int index = mIndexMap.get(device, -1);
if (index == -1) {
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
@@ -9788,7 +9804,7 @@ public class AudioService extends IAudioService.Stub
}
public @NonNull VolumeInfo getVolumeInfo(int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
int index = mIndexMap.get(device, -1);
if (index == -1) {
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
@@ -9805,7 +9821,7 @@ public class AudioService extends IAudioService.Stub
}
public boolean hasIndexForDevice(int device) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
return (mIndexMap.get(device, -1) != -1);
}
}
@@ -9837,8 +9853,8 @@ public class AudioService extends IAudioService.Stub
* @param srcStream
* @param caller
*/
- // must be sync'd on mSettingsLock before VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
+ // must be sync'd on mSettingsLock before mVolumeStateLock
+ @GuardedBy("mVolumeStateLock")
public void setAllIndexes(VolumeStreamState srcStream, String caller) {
if (srcStream == null || mStreamType == srcStream.mStreamType) {
return;
@@ -9862,8 +9878,8 @@ public class AudioService extends IAudioService.Stub
}
}
- // must be sync'd on mSettingsLock before VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
+ // must be sync'd on mSettingsLock before mVolumeStateLock
+ @GuardedBy("mVolumeStateLock")
public void setAllIndexesToMax() {
for (int i = 0; i < mIndexMap.size(); i++) {
mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
@@ -9876,7 +9892,7 @@ public class AudioService extends IAudioService.Stub
// vss.setIndex which grabs this lock after VSS.class. Locking order needs to be
// preserved
synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (mVolumeGroupState != null) {
int groupIndex = (getIndex(device) + 5) / 10;
if (DEBUG_VOL) {
@@ -9908,7 +9924,7 @@ public class AudioService extends IAudioService.Stub
*/
public boolean mute(boolean state, String source) {
boolean changed = false;
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
changed = mute(state, true, source);
}
if (changed) {
@@ -9924,7 +9940,7 @@ public class AudioService extends IAudioService.Stub
*/
public boolean muteInternally(boolean state) {
boolean changed = false;
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
if (state != mIsMutedInternally) {
changed = true;
mIsMutedInternally = state;
@@ -9939,7 +9955,7 @@ public class AudioService extends IAudioService.Stub
return changed;
}
- @GuardedBy("VolumeStreamState.class")
+ @GuardedBy("mVolumeStateLock")
public boolean isFullyMuted() {
return mIsMuted || mIsMutedInternally;
}
@@ -9960,7 +9976,7 @@ public class AudioService extends IAudioService.Stub
*/
public boolean mute(boolean state, boolean apply, String src) {
boolean changed;
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
changed = state != mIsMuted;
if (changed) {
sMuteLogger.enqueue(
@@ -9994,7 +10010,7 @@ public class AudioService extends IAudioService.Stub
}
public void doMute() {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// If associated to volume group, update group cache
updateVolumeGroupIndex(getDeviceForStream(mStreamType), /* forceMuteState= */true);
@@ -10015,7 +10031,7 @@ public class AudioService extends IAudioService.Stub
}
public void checkFixedVolumeDevices() {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
// ignore settings for fixed volume devices: volume should always be at max or 0
if (sStreamVolumeAlias.get(mStreamType) == AudioSystem.STREAM_MUSIC) {
for (int i = 0; i < mIndexMap.size(); i++) {
@@ -10186,8 +10202,7 @@ public class AudioService extends IAudioService.Stub
}
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
-
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_QUEUE,
device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device) ? 1 : 0),
@@ -12191,7 +12206,7 @@ public class AudioService extends IAudioService.Stub
mCameraSoundForced = cameraSoundForced;
if (cameraSoundForcedChanged) {
if (!mIsSingleVolume) {
- synchronized (VolumeStreamState.class) {
+ synchronized (mVolumeStateLock) {
final VolumeStreamState s = getVssForStreamOrDefault(
AudioSystem.STREAM_SYSTEM_ENFORCED);
if (cameraSoundForced) {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index e86c34cab88a..a6267c156fb3 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -367,9 +367,9 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
* @return
*/
public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state,
- int codecFormat) {
+ int codecFormat, boolean deviceSwitch) {
invalidateRoutingCache();
- return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat);
+ return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat, deviceSwitch);
}
/**
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 922116999bc7..844e3524384d 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -26,6 +26,8 @@ import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
+import static com.android.media.audio.Flags.optimizeBtDeviceSwitch;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothA2dp;
@@ -393,8 +395,11 @@ public class BtHelper {
+ "received with null profile proxy for device: "
+ btDevice)).printLog(TAG));
return;
+
}
- onSetBtScoActiveDevice(btDevice);
+ boolean deviceSwitch = optimizeBtDeviceSwitch()
+ && btDevice != null && mBluetoothHeadsetDevice != null;
+ onSetBtScoActiveDevice(btDevice, deviceSwitch);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
onScoAudioStateChanged(btState);
@@ -814,7 +819,7 @@ public class BtHelper {
if (device == null) {
continue;
}
- onSetBtScoActiveDevice(device);
+ onSetBtScoActiveDevice(device, false /*deviceSwitch*/);
}
} else {
Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter");
@@ -907,7 +912,8 @@ public class BtHelper {
}
@GuardedBy("mDeviceBroker.mDeviceStateLock")
- private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
+ private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive,
+ boolean deviceSwitch) {
if (btDevice == null) {
return true;
}
@@ -919,12 +925,12 @@ public class BtHelper {
if (isActive) {
audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
result = mDeviceBroker.handleDeviceConnection(
- audioDevice, true /*connect*/, btDevice);
+ audioDevice, true /*connect*/, btDevice, false /*deviceSwitch*/);
} else {
AudioDeviceAttributes ada = mResolvedScoAudioDevices.get(btDevice);
if (ada != null) {
result = mDeviceBroker.handleDeviceConnection(
- ada, false /*connect*/, btDevice);
+ ada, false /*connect*/, btDevice, deviceSwitch);
} else {
// Disconnect all possible audio device types if the disconnected device type is
// unknown
@@ -935,7 +941,8 @@ public class BtHelper {
};
for (int outDeviceType : outDeviceTypes) {
result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
- outDeviceType, address, name), false /*connect*/, btDevice);
+ outDeviceType, address, name), false /*connect*/, btDevice,
+ deviceSwitch);
}
}
}
@@ -944,7 +951,7 @@ public class BtHelper {
// handleDeviceConnection() && result to make sure the method get executed
result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
inDevice, address, name),
- isActive, btDevice) && result;
+ isActive, btDevice, deviceSwitch) && result;
if (result) {
if (isActive) {
mResolvedScoAudioDevices.put(btDevice, audioDevice);
@@ -961,18 +968,18 @@ public class BtHelper {
}
@GuardedBy("mDeviceBroker.mDeviceStateLock")
- /*package */ void onSetBtScoActiveDevice(BluetoothDevice btDevice) {
+ /*package */ void onSetBtScoActiveDevice(BluetoothDevice btDevice, boolean deviceSwitch) {
Log.i(TAG, "onSetBtScoActiveDevice: " + getAnonymizedAddress(mBluetoothHeadsetDevice)
- + " -> " + getAnonymizedAddress(btDevice));
+ + " -> " + getAnonymizedAddress(btDevice) + ", deviceSwitch: " + deviceSwitch);
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
if (Objects.equals(btDevice, previousActiveDevice)) {
return;
}
- if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
+ if (!handleBtScoActiveDeviceChange(previousActiveDevice, false, deviceSwitch)) {
Log.w(TAG, "onSetBtScoActiveDevice() failed to remove previous device "
+ getAnonymizedAddress(previousActiveDevice));
}
- if (!handleBtScoActiveDeviceChange(btDevice, true)) {
+ if (!handleBtScoActiveDeviceChange(btDevice, true, false /*deviceSwitch*/)) {
Log.e(TAG, "onSetBtScoActiveDevice() failed to add new device "
+ getAnonymizedAddress(btDevice));
// set mBluetoothHeadsetDevice to null when failing to add new device
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index b11267ef8634..79523bd02404 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -69,6 +69,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final String SYSTEM_GENDER_HELPER = "system_gender";
private static final String DISPLAY_HELPER = "display";
private static final String INPUT_HELPER = "input";
+ private static final String WEAR_BACKUP_HELPER = "wear";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -113,7 +114,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
private static final Set<String> sEligibleHelpersForNonSystemUser =
SetUtils.union(sEligibleHelpersForProfileUser,
Sets.newArraySet(ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER, PREFERRED_HELPER,
- SHORTCUT_MANAGER_HELPER, INPUT_HELPER));
+ SHORTCUT_MANAGER_HELPER, INPUT_HELPER, WEAR_BACKUP_HELPER));
private int mUserId = UserHandle.USER_SYSTEM;
private boolean mIsProfileUser = false;
@@ -153,6 +154,11 @@ public class SystemBackupAgent extends BackupAgentHelper {
if (com.android.hardware.input.Flags.enableBackupAndRestoreForInputGestures()) {
addHelperIfEligibleForUser(INPUT_HELPER, new InputBackupHelper(mUserId));
}
+
+ // Add Wear helper only if the device is a watch
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ addHelperIfEligibleForUser(WEAR_BACKUP_HELPER, new WearBackupHelper());
+ }
}
@Override
diff --git a/services/core/java/com/android/server/backup/WearBackupHelper.java b/services/core/java/com/android/server/backup/WearBackupHelper.java
new file mode 100644
index 000000000000..27416b3eb2a6
--- /dev/null
+++ b/services/core/java/com/android/server/backup/WearBackupHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://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.backup;
+
+import android.annotation.Nullable;
+import android.app.backup.BlobBackupHelper;
+
+import com.android.server.LocalServices;
+
+/** A {@link android.app.backup.BlobBackupHelper} for Wear */
+public class WearBackupHelper extends BlobBackupHelper {
+
+ private static final int BLOB_VERSION = 1;
+ private static final String KEY_WEAR_BACKUP = "wear";
+ @Nullable private final WearBackupInternal mWearBackupInternal;
+
+ public WearBackupHelper() {
+ super(BLOB_VERSION, KEY_WEAR_BACKUP);
+ mWearBackupInternal = LocalServices.getService(WearBackupInternal.class);
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ return KEY_WEAR_BACKUP.equals(key) && mWearBackupInternal != null
+ ? mWearBackupInternal.getBackupPayload(getLogger())
+ : null;
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (KEY_WEAR_BACKUP.equals(key) && mWearBackupInternal != null) {
+ mWearBackupInternal.applyRestoredPayload(payload);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/backup/WearBackupInternal.java b/services/core/java/com/android/server/backup/WearBackupInternal.java
new file mode 100644
index 000000000000..7b4847b51df6
--- /dev/null
+++ b/services/core/java/com/android/server/backup/WearBackupInternal.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://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.backup;
+
+import android.app.backup.BackupRestoreEventLogger;
+
+import com.android.internal.annotations.Keep;
+
+/** A local service internal for Wear OS handle backup/restore */
+@Keep
+public interface WearBackupInternal {
+
+ /** Gets the backup payload */
+ byte[] getBackupPayload(BackupRestoreEventLogger logger);
+
+ /** Applies the restored payload */
+ void applyRestoredPayload(byte[] payload);
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java b/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java
index 000ee5446962..9f364677705e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java
+++ b/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java
@@ -20,12 +20,16 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import android.annotation.NonNull;
import android.hardware.SensorPrivacyManager;
+import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraManager;
+import android.util.Log;
import java.util.concurrent.ConcurrentHashMap;
public class BiometricCameraManagerImpl implements BiometricCameraManager {
+ private static final String TAG = "BiometricCameraManager";
+
private final CameraManager mCameraManager;
private final SensorPrivacyManager mSensorPrivacyManager;
private final ConcurrentHashMap<String, Boolean> mIsCameraAvailable = new ConcurrentHashMap<>();
@@ -52,12 +56,18 @@ public class BiometricCameraManagerImpl implements BiometricCameraManager {
@Override
public boolean isAnyCameraUnavailable() {
- for (String cameraId : mIsCameraAvailable.keySet()) {
- if (!mIsCameraAvailable.get(cameraId)) {
- return true;
+ try {
+ for (String cameraId : mCameraManager.getCameraIdList()) {
+ if (!mIsCameraAvailable.getOrDefault(cameraId, true)) {
+ return true;
+ }
}
+ return false;
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Camera exception thrown when trying to determine availability: ", e);
+ //If face HAL is unable to get access to a camera, it will return an error.
+ return false;
}
- return false;
}
@Override
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index d412277d2605..f5284a3ed589 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -211,4 +211,20 @@ public abstract class VirtualDeviceManagerInternal {
*/
public abstract @NonNull VirtualDeviceManager.VirtualDevice createVirtualDevice(
@NonNull VirtualDeviceParams params);
+
+ /**
+ * Returns the details of the virtual device with the given ID, if any.
+ *
+ * <p>The returned object is a read-only representation of the virtual device that expose its
+ * properties.</p>
+ *
+ * <p>Note that if the virtual device is closed and becomes invalid, the returned object will
+ * not be updated and may contain stale values. Use a {@link VirtualDeviceListener} for real
+ * time updates of the availability of virtual devices.</p>
+ *
+ * @return the virtual device with the requested ID, or {@code null} if no such device exists or
+ * it has already been closed.
+ */
+ @Nullable
+ public abstract VirtualDevice getVirtualDevice(int deviceId);
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 88f5c81231b8..c41b8db1ce75 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -333,7 +333,7 @@ public class AutomaticBrightnessController {
int ambientLightHorizonLong, float userLux, float userNits,
DisplayManagerFlags displayManagerFlags) {
mInjector = injector;
- mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness());
+ mClock = injector.createClock();
mContext = context;
mCallbacks = callbacks;
mSensorManager = sensorManager;
@@ -1402,8 +1402,7 @@ public class AutomaticBrightnessController {
public void onSensorChanged(SensorEvent event) {
if (mLightSensorEnabled) {
// The time received from the sensor is in nano seconds, hence changing it to ms
- final long time = (mDisplayManagerFlags.offloadControlsDozeAutoBrightness())
- ? TimeUnit.NANOSECONDS.toMillis(event.timestamp) : mClock.uptimeMillis();
+ final long time = TimeUnit.NANOSECONDS.toMillis(event.timestamp);
final float lux = event.values[0];
handleLightSensorEvent(time, lux);
}
@@ -1616,20 +1615,13 @@ public class AutomaticBrightnessController {
}
private static class RealClock implements Clock {
- private final boolean mOffloadControlsDozeBrightness;
-
- RealClock(boolean offloadControlsDozeBrightness) {
- mOffloadControlsDozeBrightness = offloadControlsDozeBrightness;
- }
-
@Override
public long uptimeMillis() {
return SystemClock.uptimeMillis();
}
public long getSensorEventScaleTime() {
- return (mOffloadControlsDozeBrightness)
- ? SystemClock.elapsedRealtime() : uptimeMillis();
+ return SystemClock.elapsedRealtime();
}
}
@@ -1638,8 +1630,8 @@ public class AutomaticBrightnessController {
return BackgroundThread.getHandler();
}
- Clock createClock(boolean offloadControlsDozeBrightness) {
- return new RealClock(offloadControlsDozeBrightness);
+ Clock createClock() {
+ return new RealClock();
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b6a3f4041b13..258c95582e3a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -812,7 +812,7 @@ public final class DisplayManagerService extends SystemService {
handleMinimalPostProcessingAllowedSettingChange();
if (mFlags.isDisplayContentModeManagementEnabled()) {
- updateMirrorBuiltInDisplaySettingLocked();
+ updateMirrorBuiltInDisplaySettingLocked(/*shouldSendDisplayChangeEvent=*/ true);
}
final UserManager userManager = getUserManager();
@@ -868,7 +868,7 @@ public final class DisplayManagerService extends SystemService {
updateHdrConversionModeSettingsLocked();
}
if (mFlags.isDisplayContentModeManagementEnabled()) {
- updateMirrorBuiltInDisplaySettingLocked();
+ updateMirrorBuiltInDisplaySettingLocked(/*shouldSendDisplayChangeEvent=*/ false);
}
}
@@ -1237,8 +1237,11 @@ public final class DisplayManagerService extends SystemService {
}
if (Settings.Secure.getUriFor(MIRROR_BUILT_IN_DISPLAY).equals(uri)) {
- if (mFlags.isDisplayContentModeManagementEnabled()) {
- updateMirrorBuiltInDisplaySettingLocked();
+ synchronized (mSyncRoot) {
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ updateMirrorBuiltInDisplaySettingLocked(/*shouldSendDisplayChangeEvent=*/
+ true);
+ }
}
return;
}
@@ -1258,18 +1261,19 @@ public final class DisplayManagerService extends SystemService {
1, UserHandle.USER_CURRENT) != 0);
}
- private void updateMirrorBuiltInDisplaySettingLocked() {
- synchronized (mSyncRoot) {
- ContentResolver resolver = mContext.getContentResolver();
- final boolean mirrorBuiltInDisplay = Settings.Secure.getIntForUser(resolver,
- MIRROR_BUILT_IN_DISPLAY, 0, UserHandle.USER_CURRENT) != 0;
- if (mMirrorBuiltInDisplay == mirrorBuiltInDisplay) {
- return;
- }
- mMirrorBuiltInDisplay = mirrorBuiltInDisplay;
- if (mFlags.isDisplayContentModeManagementEnabled()) {
- mLogicalDisplayMapper.forEachLocked(this::updateCanHostTasksIfNeededLocked);
- }
+ private void updateMirrorBuiltInDisplaySettingLocked(boolean shouldSendDisplayChangeEvent) {
+ ContentResolver resolver = mContext.getContentResolver();
+ final boolean mirrorBuiltInDisplay = Settings.Secure.getIntForUser(resolver,
+ MIRROR_BUILT_IN_DISPLAY, 0, UserHandle.USER_CURRENT) != 0;
+ if (mMirrorBuiltInDisplay == mirrorBuiltInDisplay) {
+ return;
+ }
+ mMirrorBuiltInDisplay = mirrorBuiltInDisplay;
+ if (mFlags.isDisplayContentModeManagementEnabled()) {
+ mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
+ updateCanHostTasksIfNeededLocked(logicalDisplay,
+ shouldSendDisplayChangeEvent);
+ });
}
}
@@ -2380,7 +2384,7 @@ public final class DisplayManagerService extends SystemService {
new BrightnessPair(brightnessDefault, brightnessDefault));
if (mFlags.isDisplayContentModeManagementEnabled()) {
- updateCanHostTasksIfNeededLocked(display);
+ updateCanHostTasksIfNeededLocked(display, /*shouldSendDisplayChangeEvent=*/ false);
}
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -2587,6 +2591,11 @@ public final class DisplayManagerService extends SystemService {
sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED);
}
+ private void handleLogicalDisplayCommittedStateChangedLocked(@NonNull LogicalDisplay display) {
+ sendDisplayEventIfEnabledLocked(display,
+ DisplayManagerGlobal.EVENT_DISPLAY_COMMITTED_STATE_CHANGED);
+ }
+
private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) {
mDisplayModeDirector.defaultDisplayDeviceUpdated(display.getPrimaryDisplayDeviceLocked()
.mDisplayDeviceConfig);
@@ -2609,7 +2618,8 @@ public final class DisplayManagerService extends SystemService {
// Blank or unblank the display immediately to match the state requested
// by the display power controller (if known).
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0
+ || android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
if (display == null) {
return null;
@@ -2697,8 +2707,9 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void updateCanHostTasksIfNeededLocked(LogicalDisplay display) {
- if (display.setCanHostTasksLocked(!mMirrorBuiltInDisplay)) {
+ private void updateCanHostTasksIfNeededLocked(LogicalDisplay display,
+ boolean shouldSendDisplayChangeEvent) {
+ if (display.setCanHostTasksLocked(!mMirrorBuiltInDisplay) && shouldSendDisplayChangeEvent) {
sendDisplayEventIfEnabledLocked(display,
DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
}
@@ -4165,6 +4176,9 @@ public final class DisplayManagerService extends SystemService {
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_STATE_CHANGED:
handleLogicalDisplayStateChangedLocked(display);
break;
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED:
+ handleLogicalDisplayCommittedStateChangedLocked(display);
+ break;
}
}
@@ -4419,6 +4433,9 @@ public final class DisplayManagerService extends SystemService {
case DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED:
return (mask & DisplayManagerGlobal
.INTERNAL_EVENT_FLAG_DISPLAY_STATE) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_COMMITTED_STATE_CHANGED:
+ return (mask & DisplayManagerGlobal
+ .INTERNAL_EVENT_FLAG_DISPLAY_COMMITTED_STATE_CHANGED) != 0;
default:
// This should never happen.
Slog.e(TAG, "Unknown display event " + event);
@@ -5563,7 +5580,9 @@ public final class DisplayManagerService extends SystemService {
final DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked(
id).getPrimaryDisplayDeviceLocked();
final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
- if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
+ if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0
+ || android.companion.virtualdevice.flags.Flags
+ .correctVirtualDisplayPowerState()) {
final DisplayPowerController displayPowerController =
mDisplayPowerControllers.get(id);
if (displayPowerController != null) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 02db051dff57..872f33484951 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -91,6 +91,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
public static final int LOGICAL_DISPLAY_EVENT_DISCONNECTED = 1 << 8;
public static final int LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED = 1 << 9;
public static final int LOGICAL_DISPLAY_EVENT_STATE_CHANGED = 1 << 10;
+ public static final int LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED = 1 << 11;
+
public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
@@ -810,7 +812,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
int logicalDisplayEventMask = mLogicalDisplaysToUpdate
.get(displayId, LOGICAL_DISPLAY_EVENT_BASE);
boolean hasBasicInfoChanged =
- !mTempDisplayInfo.equals(newDisplayInfo, /* compareRefreshRate */ false);
+ !mTempDisplayInfo.equals(newDisplayInfo, /* compareOnlyBasicChanges */ true);
// The display is no longer valid and needs to be removed.
if (!display.isValidLocked()) {
// Remove from group
@@ -930,6 +932,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_BASIC_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_STATE_CHANGED);
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CONNECTED);
@@ -961,6 +964,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
&& mTempDisplayInfo.state != newDisplayInfo.state) {
mask |= LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
}
+
+ if (mFlags.isCommittedStateSeparateEventEnabled()
+ && mTempDisplayInfo.committedState != newDisplayInfo.committedState) {
+ mask |= LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED;
+ }
return mask;
}
/**
@@ -1360,6 +1368,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return "disconnected";
case LOGICAL_DISPLAY_EVENT_STATE_CHANGED:
return "state_changed";
+ case LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED:
+ return "committed_state_changed";
case LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED:
return "refresh_rate_changed";
case LOGICAL_DISPLAY_EVENT_BASIC_CHANGED:
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 4779b690adfb..e7939bb50ece 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -371,7 +371,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mCallback = callback;
mProjection = projection;
mMediaProjectionCallback = mediaProjectionCallback;
- mDisplayState = Display.STATE_ON;
+ if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) {
+ // The display's power state depends on the power state of the state of its
+ // display / power group, which we don't know here. Initializing to UNKNOWN allows
+ // the first call to requestDisplayStateLocked() to set the correct state.
+ // This also triggers VirtualDisplay.Callback to tell the owner the initial state.
+ mDisplayState = Display.STATE_UNKNOWN;
+ } else {
+ mDisplayState = Display.STATE_ON;
+ }
mPendingChanges |= PENDING_SURFACE_CHANGE;
mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
@@ -564,14 +572,23 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mInfo.yDpi = mDensityDpi;
mInfo.presentationDeadlineNanos = 1000000000L / (int) getRefreshRate(); // 1 frame
mInfo.flags = 0;
- if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
- | DisplayDeviceInfo.FLAG_NEVER_BLANK;
- }
- if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
- mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) {
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
+ }
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+ }
} else {
- mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
+ | DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ }
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
+ mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK;
+ } else {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+ }
}
if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 2c6f37448735..6510441ba28f 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -291,8 +291,7 @@ public class DisplayBrightnessStrategySelector {
void setAllowAutoBrightnessWhileDozing(
DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
mAllowAutoBrightnessWhileDozing = mAllowAutoBrightnessWhileDozingConfig;
- if (mDisplayManagerFlags.offloadControlsDozeAutoBrightness()
- && mDisplayManagerFlags.isDisplayOffloadEnabled()
+ if (mDisplayManagerFlags.isDisplayOffloadEnabled()
&& displayOffloadSession != null) {
mAllowAutoBrightnessWhileDozing &= displayOffloadSession.allowAutoBrightnessInDoze();
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index bc5d90599b41..c3057ded66eb 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -156,11 +156,6 @@ public class DisplayManagerFlags {
Flags.FLAG_DOZE_BRIGHTNESS_FLOAT,
Flags::dozeBrightnessFloat);
- private final FlagState mOffloadControlsDozeAutoBrightness = new FlagState(
- Flags.FLAG_OFFLOAD_CONTROLS_DOZE_AUTO_BRIGHTNESS,
- Flags::offloadControlsDozeAutoBrightness
- );
-
private final FlagState mPeakRefreshRatePhysicalLimit = new FlagState(
Flags.FLAG_ENABLE_PEAK_REFRESH_RATE_PHYSICAL_LIMIT,
Flags::enablePeakRefreshRatePhysicalLimit
@@ -280,6 +275,11 @@ public class DisplayManagerFlags {
Flags::refreshRateEventForForegroundApps
);
+ private final FlagState mCommittedStateSeparateEvent = new FlagState(
+ Flags.FLAG_COMMITTED_STATE_SEPARATE_EVENT,
+ Flags::committedStateSeparateEvent
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -435,13 +435,6 @@ public class DisplayManagerFlags {
return mDozeBrightnessFloat.isEnabled();
}
- /**
- * @return Whether DisplayOffload should control auto-brightness in doze
- */
- public boolean offloadControlsDozeAutoBrightness() {
- return mOffloadControlsDozeAutoBrightness.isEnabled();
- }
-
public boolean isPeakRefreshRatePhysicalLimitEnabled() {
return mPeakRefreshRatePhysicalLimit.isEnabled();
}
@@ -603,6 +596,14 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if the flag for having a separate event for display's committed state
+ * is enabled
+ */
+ public boolean isCommittedStateSeparateEventEnabled() {
+ return mCommittedStateSeparateEvent.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -634,7 +635,6 @@ public class DisplayManagerFlags {
pw.println(" " + mResolutionBackupRestore);
pw.println(" " + mUseFusionProxSensor);
pw.println(" " + mDozeBrightnessFloat);
- pw.println(" " + mOffloadControlsDozeAutoBrightness);
pw.println(" " + mPeakRefreshRatePhysicalLimit);
pw.println(" " + mIgnoreAppPreferredRefreshRate);
pw.println(" " + mSynthetic60hzModes);
@@ -659,6 +659,7 @@ public class DisplayManagerFlags {
pw.println(" " + mBaseDensityForExternalDisplays);
pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
pw.println(" " + mRefreshRateEventForForegroundApps);
+ pw.println(" " + mCommittedStateSeparateEvent);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 8211febade60..f5451307afb7 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -255,17 +255,6 @@ flag {
}
flag {
- name: "offload_controls_doze_auto_brightness"
- namespace: "display_manager"
- description: "Allows the registered DisplayOffloader to control if auto-brightness is used in doze"
- bug: "327392714"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_peak_refresh_rate_physical_limit"
namespace: "display_manager"
description: "Flag for adding physical refresh rate limit if smooth display setting is on "
@@ -456,9 +445,8 @@ flag {
flag {
name: "enable_display_content_mode_management"
namespace: "lse_desktop_experience"
- description: "Enable switching the content mode of connected displays between mirroring and extened. Also change the default content mode to extended mode."
+ description: "Enable switching the content mode of connected displays between mirroring and extended. Also change the default content mode to extended mode."
bug: "378385869"
- is_fixed_read_only: true
}
flag {
@@ -509,3 +497,14 @@ flag {
bug: "293651324"
is_fixed_read_only: false
}
+
+flag {
+ name: "committed_state_separate_event"
+ namespace: "display_manager"
+ description: "Move Display committed state into a separate event"
+ bug: "342192387"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
index 94842041af82..ab86433ca50d 100644
--- a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
@@ -58,9 +58,9 @@ public interface AudioDeviceVolumeManagerWrapper {
void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
+ boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
- @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
- boolean handlesVolumeAdjustment);
+ @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener);
/**
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@@ -69,7 +69,7 @@ public interface AudioDeviceVolumeManagerWrapper {
void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
+ boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
- @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
- boolean handlesVolumeAdjustment);
+ @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener);
}
diff --git a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
index ff99ace38ef0..10cbb00d2398 100644
--- a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
@@ -61,21 +61,21 @@ public class DefaultAudioDeviceVolumeManagerWrapper
public void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
+ boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
- @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
- boolean handlesVolumeAdjustment) {
- mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeBehavior(device, volume, executor,
- vclistener, handlesVolumeAdjustment);
+ @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener) {
+ mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeBehavior(device, volume,
+ handlesVolumeAdjustment, executor, vclistener);
}
@Override
public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
+ boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
- @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener,
- boolean handlesVolumeAdjustment) {
+ @NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener) {
mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeAdjustOnlyBehavior(device, volume,
- executor, vclistener, handlesVolumeAdjustment);
+ handlesVolumeAdjustment, executor, vclistener);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 3d6d34bf9911..3cb21c3e2697 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1404,6 +1404,9 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (connected) {
if (mArcEstablished) {
enableAudioReturnChannel(true);
+ } else {
+ HdmiLogger.debug("Restart ARC again");
+ onNewAvrAdded(getAvrDeviceInfo());
}
} else {
enableAudioReturnChannel(false);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 89f0d0edbf2b..6d973ac8d1b5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4798,15 +4798,15 @@ public class HdmiControlService extends SystemService {
Slog.d(TAG, "Enabling absolute volume behavior");
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
- device, volumeInfo, mServiceThreadExecutor,
- mAbsoluteVolumeChangedListener, true);
+ device, volumeInfo, true, mServiceThreadExecutor,
+ mAbsoluteVolumeChangedListener);
}
} else if (tv() != null) {
Slog.d(TAG, "Enabling adjust-only absolute volume behavior");
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior(
- device, volumeInfo, mServiceThreadExecutor,
- mAbsoluteVolumeChangedListener, true);
+ device, volumeInfo, true, mServiceThreadExecutor,
+ mAbsoluteVolumeChangedListener);
}
}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 977c029f3a29..d71c8a1056d9 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -146,11 +146,6 @@ final class InputGestureManager {
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
),
createKeyGesture(
- KeyEvent.KEYCODE_N,
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
- KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES
- ),
- createKeyGesture(
KeyEvent.KEYCODE_S,
KeyEvent.META_META_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 87f693cc7291..1ace41cba364 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -344,4 +344,42 @@ public abstract class InputManagerInternal {
*/
public abstract void applyBackupPayload(Map<Integer, byte[]> payload, int userId)
throws XmlPullParserException, IOException;
+
+ /**
+ * An interface for filtering pointer motion event before cursor position is determined.
+ * <p>
+ * Different from {@code android.view.InputFilter}, this filter can filter motion events at
+ * an early stage of the input pipeline, but only called for pointer's relative motion events.
+ * Unless the user really needs to filter events before the cursor position in the display is
+ * determined, use {@code android.view.InputFilter} instead.
+ */
+ public interface AccessibilityPointerMotionFilter {
+ /**
+ * Called everytime pointer's relative motion event happens.
+ * The returned dx and dy will be used to move the cursor in the display.
+ * <p>
+ * This call happens on the input hot path and it is extremely performance sensitive. It
+ * also must not call back into native code.
+ *
+ * @param dx delta x of the event in pixels.
+ * @param dy delta y of the event in pixels.
+ * @param currentX the cursor x coordinate on the screen before the motion event.
+ * @param currentY the cursor y coordinate on the screen before the motion event.
+ * @param displayId the display ID of the current cursor.
+ * @return an array of length 2, delta x and delta y after filtering the motion. The delta
+ * values are in pixels and must be between 0 and original delta.
+ */
+ @NonNull
+ float[] filterPointerMotionEvent(float dx, float dy, float currentX, float currentY,
+ int displayId);
+ }
+
+ /**
+ * Registers an {@code AccessibilityCursorFilter}.
+ *
+ * @param filter The filter to register. If a filter is already registered, the old filter is
+ * unregistered. {@code null} unregisters the filter that is already registered.
+ */
+ public abstract void registerAccessibilityPointerMotionFilter(
+ @Nullable AccessibilityPointerMotionFilter filter);
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8624f4230e9c..c2fecf283a34 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,8 +25,8 @@ import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
-import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.keyEventActivityDetection;
+import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
@@ -143,6 +143,7 @@ import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.DisplayThread;
+import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -193,15 +194,11 @@ public class InputManagerService extends IInputManager.Stub
private static final int MSG_SYSTEM_READY = 5;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
- private static final AdditionalDisplayInputProperties
- DEFAULT_ADDITIONAL_DISPLAY_INPUT_PROPERTIES = new AdditionalDisplayInputProperties();
private final NativeInputManagerService mNative;
private final Context mContext;
private final InputManagerHandler mHandler;
- @UserIdInt
- private int mCurrentUserId = UserHandle.USER_SYSTEM;
private DisplayManagerInternal mDisplayManagerInternal;
private WindowManagerInternal mWindowManagerInternal;
@@ -289,7 +286,7 @@ public class InputManagerService extends IInputManager.Stub
final Object mKeyEventActivityLock = new Object();
@GuardedBy("mKeyEventActivityLock")
- private List<IKeyEventActivityListener> mKeyEventActivityListenersToNotify =
+ private final List<IKeyEventActivityListener> mKeyEventActivityListenersToNotify =
new ArrayList<>();
// Rate limit for key event activity detection. Prevent the listener from being notified
@@ -460,16 +457,26 @@ public class InputManagerService extends IInputManager.Stub
private boolean mShowKeyPresses = false;
private boolean mShowRotaryInput = false;
+ /**
+ * A lock for the accessibility pointer motion filter. Don't call native methods while holding
+ * this lock.
+ */
+ private final Object mAccessibilityPointerMotionFilterLock = new Object();
+ private InputManagerInternal.AccessibilityPointerMotionFilter
+ mAccessibilityPointerMotionFilter = null;
+
/** Point of injection for test dependencies. */
@VisibleForTesting
static class Injector {
private final Context mContext;
private final Looper mLooper;
+ private final Looper mIoLooper;
private final UEventManager mUEventManager;
- Injector(Context context, Looper looper, UEventManager uEventManager) {
+ Injector(Context context, Looper looper, Looper ioLooper, UEventManager uEventManager) {
mContext = context;
mLooper = looper;
+ mIoLooper = ioLooper;
mUEventManager = uEventManager;
}
@@ -481,6 +488,10 @@ public class InputManagerService extends IInputManager.Stub
return mLooper;
}
+ Looper getIoLooper() {
+ return mIoLooper;
+ }
+
UEventManager getUEventManager() {
return mUEventManager;
}
@@ -501,8 +512,8 @@ public class InputManagerService extends IInputManager.Stub
}
public InputManagerService(Context context) {
- this(new Injector(context, DisplayThread.get().getLooper(), new UEventManager() {}),
- context.getSystemService(PermissionEnforcer.class));
+ this(new Injector(context, DisplayThread.get().getLooper(), IoThread.get().getLooper(),
+ new UEventManager() {}), context.getSystemService(PermissionEnforcer.class));
}
@VisibleForTesting
@@ -528,7 +539,7 @@ public class InputManagerService extends IInputManager.Stub
mStickyModifierStateController = new StickyModifierStateController();
mInputDataStore = new InputDataStore();
mKeyGestureController = new KeyGestureController(mContext, injector.getLooper(),
- mInputDataStore);
+ injector.getIoLooper(), mInputDataStore);
mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
mNative);
mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
@@ -2593,6 +2604,23 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
@SuppressWarnings("unused")
+ final float[] filterPointerMotion(float dx, float dy, float currentX, float currentY,
+ int displayId) {
+ // This call happens on the input hot path and it is extremely performance sensitive.
+ // This must not call back into native code. This is called while the
+ // PointerChoreographer's lock is held.
+ synchronized (mAccessibilityPointerMotionFilterLock) {
+ if (mAccessibilityPointerMotionFilter == null) {
+ throw new IllegalStateException(
+ "filterCursor is invoked but no callback is registered.");
+ }
+ return mAccessibilityPointerMotionFilter.filterPointerMotionEvent(dx, dy, currentX,
+ currentY, displayId);
+ }
+ }
+
+ // Native callback.
+ @SuppressWarnings("unused")
@VisibleForTesting
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
notifyKeyActivityListeners(event);
@@ -3215,7 +3243,6 @@ public class InputManagerService extends IInputManager.Stub
}
private void handleCurrentUserChanged(@UserIdInt int userId) {
- mCurrentUserId = userId;
mKeyGestureController.setCurrentUserId(userId);
}
@@ -3828,6 +3855,12 @@ public class InputManagerService extends IInputManager.Stub
payload.get(BACKUP_CATEGORY_INPUT_GESTURES), userId);
}
}
+
+ @Override
+ public void registerAccessibilityPointerMotionFilter(
+ AccessibilityPointerMotionFilter filter) {
+ InputManagerService.this.registerAccessibilityPointerMotionFilter(filter);
+ }
}
@Override
@@ -4014,6 +4047,26 @@ public class InputManagerService extends IInputManager.Stub
mPointerIconCache.setAccessibilityScaleFactor(displayId, scaleFactor);
}
+ void registerAccessibilityPointerMotionFilter(
+ InputManagerInternal.AccessibilityPointerMotionFilter filter) {
+ // `#filterPointerMotion` expects that when it's called, `mAccessibilityPointerMotionFilter`
+ // is not null.
+ // Also, to avoid potential lock contention, we shouldn't call native method while holding
+ // the lock here. Native code calls `#filterPointerMotion` while PointerChoreographer's
+ // lock is held.
+ // Thus, we must set filter before we enable the filter in native, and reset the filter
+ // after we disable the filter.
+ // This also ensures the previously installed filter isn't called after the filter is
+ // updated.
+ mNative.setAccessibilityPointerMotionFilterEnabled(false);
+ synchronized (mAccessibilityPointerMotionFilterLock) {
+ mAccessibilityPointerMotionFilter = filter;
+ }
+ if (filter != null) {
+ mNative.setAccessibilityPointerMotionFilterEnabled(true);
+ }
+ }
+
interface KeyboardBacklightControllerInterface {
default void incrementKeyboardBacklight(int deviceId) {}
default void decrementKeyboardBacklight(int deviceId) {}
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 5770a09e3b92..fba0b0453bfb 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -121,6 +121,7 @@ final class KeyGestureController {
private final Context mContext;
private final Handler mHandler;
+ private final Handler mIoHandler;
private final int mSystemPid;
private final KeyCombinationManager mKeyCombinationManager;
private final SettingsObserver mSettingsObserver;
@@ -171,9 +172,11 @@ final class KeyGestureController {
private final boolean mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
- KeyGestureController(Context context, Looper looper, InputDataStore inputDataStore) {
+ KeyGestureController(Context context, Looper looper, Looper ioLooper,
+ InputDataStore inputDataStore) {
mContext = context;
mHandler = new Handler(looper, this::handleMessage);
+ mIoHandler = new Handler(ioLooper, this::handleIoMessage);
mSystemPid = Process.myPid();
mKeyGestureHandlerRecords = new TreeMap<>((p1, p2) -> {
if (Objects.equals(p1, p2)) {
@@ -458,7 +461,7 @@ final class KeyGestureController {
userId = mCurrentUserId;
}
// Load the system user's input gestures.
- mHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
+ mIoHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
}
public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -1032,7 +1035,7 @@ final class KeyGestureController {
synchronized (mUserLock) {
mCurrentUserId = userId;
}
- mHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
+ mIoHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
}
@MainThread
@@ -1073,6 +1076,12 @@ final class KeyGestureController {
AidlKeyGestureEvent event = (AidlKeyGestureEvent) msg.obj;
notifyKeyGestureEvent(event);
break;
+ }
+ return true;
+ }
+
+ private boolean handleIoMessage(Message msg) {
+ switch (msg.what) {
case MSG_PERSIST_CUSTOM_GESTURES: {
final int userId = (Integer) msg.obj;
persistInputGestures(userId);
@@ -1083,7 +1092,6 @@ final class KeyGestureController {
loadInputGestures(userId);
break;
}
-
}
return true;
}
@@ -1144,7 +1152,7 @@ final class KeyGestureController {
final int result = mInputGestureManager.addCustomInputGesture(userId,
new InputGestureData(inputGestureData));
if (result == InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) {
- mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ mIoHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
}
return result;
}
@@ -1156,7 +1164,7 @@ final class KeyGestureController {
final int result = mInputGestureManager.removeCustomInputGesture(userId,
new InputGestureData(inputGestureData));
if (result == InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) {
- mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ mIoHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
}
return result;
}
@@ -1165,7 +1173,7 @@ final class KeyGestureController {
public void removeAllCustomInputGestures(@UserIdInt int userId,
@Nullable InputGestureData.Filter filter) {
mInputGestureManager.removeAllCustomInputGestures(userId, filter);
- mHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
+ mIoHandler.obtainMessage(MSG_PERSIST_CUSTOM_GESTURES, userId).sendToTarget();
}
@BinderThread
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index f34338a397db..32409d39db3b 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -315,6 +315,16 @@ interface NativeInputManagerService {
*/
boolean setKernelWakeEnabled(int deviceId, boolean enabled);
+ /**
+ * Set whether the accessibility pointer motion filter is enabled.
+ * <p>
+ * Once enabled, {@link InputManagerService#filterPointerMotion} is called for evety motion
+ * event from pointer devices.
+ *
+ * @param enabled {@code true} if the filter is enabled, {@code false} otherwise.
+ */
+ void setAccessibilityPointerMotionFilterEnabled(boolean enabled);
+
/** The native implementation of InputManagerService methods. */
class NativeImpl implements NativeInputManagerService {
/** Pointer to native input manager service object, used by native code. */
@@ -628,5 +638,8 @@ interface NativeInputManagerService {
@Override
public native boolean setKernelWakeEnabled(int deviceId, boolean enabled);
+
+ @Override
+ public native void setAccessibilityPointerMotionFilterEnabled(boolean enabled);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 484b47022f04..dfdd9e54fe2e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -365,7 +365,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
return mCurrentImeUserId;
}
- /**
+ /**
* Figures out the target IME user ID associated with the given {@code displayId}.
*
* @param displayId the display ID to be queried about
@@ -649,12 +649,25 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
visibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
accessibilitySoftKeyboardSetting);
if (visibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
- hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
- 0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, userId);
+ if (Flags.refactorInsetsController()) {
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, userId);
+ setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
+ } else {
+ hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
+ 0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE,
+ userId);
+ }
} else if (isShowRequestedForCurrentWindow(userId)) {
- showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
- InputMethodManager.SHOW_IMPLICIT,
- SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);
+ if (Flags.refactorInsetsController()) {
+ final var statsToken = createStatsTokenForFocusedClient(true /* show */,
+ SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);
+ setImeVisibilityOnFocusedWindowClient(true, userData, statsToken);
+ } else {
+ showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
+ InputMethodManager.SHOW_IMPLICIT,
+ SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);
+ }
}
break;
}
@@ -1319,8 +1332,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
// Do not reset the default (current) IME when it is a 3rd-party IME
String selectedMethodId = bindingController.getSelectedMethodId();
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- if (selectedMethodId != null && settings.getMethodMap().get(selectedMethodId) != null
- && !settings.getMethodMap().get(selectedMethodId).isSystem()) {
+ final InputMethodInfo selectedImi = settings.getMethodMap().get(selectedMethodId);
+ if (selectedImi != null && !selectedImi.isSystem()) {
return;
}
final List<InputMethodInfo> suitableImes = InputMethodInfoUtils.getDefaultEnabledImes(
@@ -3472,7 +3485,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
|| (windowPerceptible != null && windowPerceptible == perceptible)) {
return;
}
- mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
+ mFocusedWindowPerceptible.put(windowToken, perceptible);
updateSystemUiLocked(userId);
}
});
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 2ea61171b9e1..1a29150cd40c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -374,6 +374,34 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(mEndpointInfo).append(", ");
+ sb.append("package: ").append(mPackageName).append(", ");
+ synchronized (mWakeLock) {
+ sb.append("wakelock: ").append(mWakeLock);
+ }
+ synchronized (mOpenSessionLock) {
+ if (mSessionInfoMap.size() != 0) {
+ sb.append(System.lineSeparator());
+ sb.append(" sessions: ");
+ sb.append(System.lineSeparator());
+ }
+ for (int i = 0; i < mSessionInfoMap.size(); i++) {
+ int id = mSessionInfoMap.keyAt(i);
+ int count = i + 1;
+ sb.append(
+ " " + count + ". id="
+ + id
+ + ", remote:"
+ + mSessionInfoMap.get(id).getRemoteEndpointInfo());
+ sb.append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
/**
* Registers this endpoints with the Context Hub HAL.
*
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 06aeb62a28b8..30bb8f3fa188 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.annotation.IntDef;
import android.content.Context;
import android.hardware.contexthub.ContextHubInfo;
import android.hardware.contexthub.EndpointInfo;
@@ -34,8 +35,11 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -105,6 +109,48 @@ import java.util.function.Consumer;
/** The interface for endpoint communication (retrieved from HAL in init()) */
private IEndpointCommunication mHubInterface = null;
+ /*
+ * The list of previous registration records.
+ */
+ private static final int NUM_CLIENT_RECORDS = 20;
+ private final ConcurrentLinkedEvictingDeque<RegistrationRecord> mRegistrationRecordDeque =
+ new ConcurrentLinkedEvictingDeque<>(NUM_CLIENT_RECORDS);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"ACTION_"},
+ value = {
+ ACTION_REGISTERED,
+ ACTION_UNREGISTERED,
+ })
+ public @interface Action {}
+
+ public static final int ACTION_REGISTERED = 0;
+ public static final int ACTION_UNREGISTERED = 1;
+
+ /** A container class to store a record of ContextHubEndpointBroker registrations. */
+ private class RegistrationRecord {
+ private final String mBroker;
+ private final int mAction;
+ private final long mTimestamp;
+
+ RegistrationRecord(String broker, @Action int action) {
+ mBroker = broker;
+ mAction = action;
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ContextHubServiceUtil.formatDateFromTimestamp(mTimestamp));
+ sb.append(" ");
+ sb.append(mAction == ACTION_REGISTERED ? "+ " : "- ");
+ sb.append(mBroker);
+ return sb.toString();
+ }
+ }
+
/* package */ ContextHubEndpointManager(
Context context,
IContextHubWrapper contextHubProxy,
@@ -228,6 +274,7 @@ import java.util.function.Consumer;
return null;
}
+ mRegistrationRecordDeque.add(new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
Log.d(TAG, "Registered endpoint with ID = " + endpointId);
return IContextHubEndpoint.Stub.asInterface(broker);
}
@@ -274,7 +321,11 @@ import java.util.function.Consumer;
* @param endpointId The ID of the endpoint to unregister.
*/
/* package */ void unregisterEndpoint(long endpointId) {
- mEndpointMap.remove(endpointId);
+ ContextHubEndpointBroker broker = mEndpointMap.remove(endpointId);
+ if (broker != null) {
+ mRegistrationRecordDeque.add(
+ new RegistrationRecord(broker.toString(), ACTION_UNREGISTERED));
+ }
}
/** Invoked by the service when the Context Hub HAL restarts. */
@@ -366,6 +417,27 @@ import java.util.function.Consumer;
}
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ int count = 1;
+ for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+ sb.append(count + ". " + broker);
+ sb.append(System.lineSeparator());
+ count++;
+ }
+
+ sb.append(System.lineSeparator());
+ sb.append("Registration History:");
+ sb.append(System.lineSeparator());
+ Iterator<RegistrationRecord> it = mRegistrationRecordDeque.descendingIterator();
+ while (it.hasNext()) {
+ sb.append(it.next());
+ sb.append(System.lineSeparator());
+ }
+ return sb.toString();
+ }
+
/**
* Invokes a callback for a session with matching ID.
*
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 502a7aeba258..bf7351cb11db 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1579,6 +1579,12 @@ public class ContextHubService extends IContextHubService.Stub {
pw.println("=================== CLIENTS ====================");
pw.println(mClientManager);
+ if (mEndpointManager != null) {
+ pw.println("");
+ pw.println("=================== ENDPOINTS ====================");
+ pw.println(mEndpointManager);
+ }
+
pw.println("");
pw.println("=================== TRANSACTIONS ====================");
pw.println(mTransactionManager);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
index bb487fb52c9f..ebf7e6bed064 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
@@ -1,4 +1,3 @@
aseemk@google.com
bozhu@google.com
dementyev@google.com
-robertberry@google.com
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 42303e042561..b735b2447486 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -141,8 +141,7 @@ final class MediaRoute2ProviderWatcher {
isSelfScanOnlyProvider |=
MediaRoute2ProviderService.CATEGORY_SELF_SCAN_ONLY.equals(category);
supportsSystemMediaRouting |=
- MediaRoute2ProviderService.SERVICE_INTERFACE_SYSTEM_MEDIA.equals(
- category);
+ MediaRoute2ProviderService.CATEGORY_SYSTEM_MEDIA.equals(category);
}
}
int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 6c0d8ad7264d..debac9436bb3 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1635,11 +1635,11 @@ class MediaRouter2ServiceImpl {
manager));
}
- List<MediaRoute2Info> routes =
- userRecord.mHandler.mLastNotifiedRoutesToPrivilegedRouters.values().stream()
- .toList();
userRecord.mHandler.sendMessage(
- obtainMessage(ManagerRecord::notifyRoutesUpdated, managerRecord, routes));
+ obtainMessage(
+ UserHandler::dispatchRoutesToManagerOnHandler,
+ userRecord.mHandler,
+ managerRecord));
}
@GuardedBy("mLock")
@@ -2119,6 +2119,9 @@ class MediaRouter2ServiceImpl {
mHasBluetoothRoutingPermission.set(checkCallerHasBluetoothPermissions(mPid, mUid));
boolean newSystemRoutingPermissionValue = hasSystemRoutingPermission();
if (oldSystemRoutingPermissionValue != newSystemRoutingPermissionValue) {
+ // TODO: b/379788233 - Ensure access to fields like
+ // mLastNotifiedRoutesToPrivilegedRouters happens on the right thread. We might need
+ // to run this on the handler.
Map<String, MediaRoute2Info> routesToReport =
newSystemRoutingPermissionValue
? mUserRecord.mHandler.mLastNotifiedRoutesToPrivilegedRouters
@@ -2543,6 +2546,8 @@ class MediaRouter2ServiceImpl {
* both system route providers and user route providers.
*
* <p>See {@link #getRouterRecords(boolean hasModifyAudioRoutingPermission)}.
+ *
+ * <p>Must be accessed on this handler's thread.
*/
private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToPrivilegedRouters =
new ArrayMap<>();
@@ -2558,6 +2563,8 @@ class MediaRouter2ServiceImpl {
* (e.g. volume changes) to non-privileged routers.
*
* <p>See {@link SystemMediaRoute2Provider#mDefaultRoute}.
+ *
+ * <p>Must be accessed on this handler's thread.
*/
private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToNonPrivilegedRouters =
new ArrayMap<>();
@@ -2800,7 +2807,7 @@ class MediaRouter2ServiceImpl {
removedRoutes));
}
- dispatchUpdates(
+ dispatchUpdatesOnHandler(
hasAddedOrModifiedRoutes,
hasRemovedRoutes,
provider.mIsSystemRouteProvider,
@@ -2822,6 +2829,13 @@ class MediaRouter2ServiceImpl {
source, providerId, routesString);
}
+ /** Notifies the given manager of the current routes. */
+ public void dispatchRoutesToManagerOnHandler(ManagerRecord managerRecord) {
+ List<MediaRoute2Info> routes =
+ mLastNotifiedRoutesToPrivilegedRouters.values().stream().toList();
+ managerRecord.notifyRoutesUpdated(routes);
+ }
+
/**
* Dispatches the latest route updates in {@link #mLastNotifiedRoutesToPrivilegedRouters}
* and {@link #mLastNotifiedRoutesToNonPrivilegedRouters} to registered {@link
@@ -2834,7 +2848,7 @@ class MediaRouter2ServiceImpl {
* @param isSystemProvider whether the latest update was caused by a system provider.
* @param defaultRoute the current default route in {@link #mSystemProvider}.
*/
- private void dispatchUpdates(
+ private void dispatchUpdatesOnHandler(
boolean hasAddedOrModifiedRoutes,
boolean hasRemovedRoutes,
boolean isSystemProvider,
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 0b8b115e65d0..91a2843ccaf7 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -120,6 +120,8 @@ public class MediaQualityService extends SystemService {
private final Object mPictureProfileLock = new Object();
// A global lock for sound profile objects.
private final Object mSoundProfileLock = new Object();
+ // A global lock for user state objects.
+ private final Object mUserStateLock = new Object();
// A global lock for ambient backlight objects.
private final Object mAmbientBacklightLock = new Object();
@@ -127,17 +129,17 @@ public class MediaQualityService extends SystemService {
super(context);
mContext = context;
mHalAmbientBacklightCallback = new HalAmbientBacklightCallback();
- mPictureProfileAdjListener = new PictureProfileAdjustmentListenerImpl(mContext);
- mSoundProfileAdjListener = new SoundProfileAdjustmentListenerImpl(mContext);
mPackageManager = mContext.getPackageManager();
mPictureProfileTempIdMap = new BiMap<>();
mSoundProfileTempIdMap = new BiMap<>();
mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
- mMqDatabaseUtils = new MqDatabaseUtils(mContext);
mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
mMediaQualityDbHelper.setIdleConnectionTimeout(30);
- mHalNotifier = new HalNotifier();
mMqManagerNotifier = new MqManagerNotifier();
+ mMqDatabaseUtils = new MqDatabaseUtils();
+ mHalNotifier = new HalNotifier();
+ mPictureProfileAdjListener = new PictureProfileAdjustmentListenerImpl();
+ mSoundProfileAdjListener = new SoundProfileAdjustmentListenerImpl();
// The package info in the context isn't initialized in the way it is for normal apps,
// so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
@@ -166,20 +168,21 @@ public class MediaQualityService extends SystemService {
if (mMediaQuality != null) {
try {
mMediaQuality.setAmbientBacklightCallback(mHalAmbientBacklightCallback);
+
+ mPpChangedListener = mMediaQuality.getPictureProfileListener();
+ mSpChangedListener = mMediaQuality.getSoundProfileListener();
+
mMediaQuality.setPictureProfileAdjustmentListener(mPictureProfileAdjListener);
mMediaQuality.setSoundProfileAdjustmentListener(mSoundProfileAdjListener);
+
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
}
}
- mPpChangedListener = IPictureProfileChangedListener.Stub.asInterface(binder);
- mSpChangedListener = ISoundProfileChangedListener.Stub.asInterface(binder);
-
publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
}
- // TODO: Add additional APIs. b/373951081
private final class BinderService extends IMediaQualityManager.Stub {
@GuardedBy("mPictureProfileLock")
@@ -225,7 +228,6 @@ public class MediaQualityService extends SystemService {
PictureProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
-
synchronized (mPictureProfileLock) {
ContentValues values = MediaQualityUtils.getContentValues(dbId,
pp.getProfileType(),
@@ -233,7 +235,6 @@ public class MediaQualityService extends SystemService {
pp.getPackageName(),
pp.getInputId(),
pp.getParameters());
-
updateDatabaseOnPictureProfileAndNotifyManagerAndHal(values, pp.getParameters());
}
}
@@ -269,12 +270,13 @@ public class MediaQualityService extends SystemService {
mMqManagerNotifier.notifyOnPictureProfileError(id,
PictureProfile.ERROR_INVALID_ARGUMENT,
Binder.getCallingUid(), Binder.getCallingPid());
+ } else {
+ mMqManagerNotifier.notifyOnPictureProfileRemoved(
+ mPictureProfileTempIdMap.getValue(dbId), toDelete,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ mPictureProfileTempIdMap.remove(dbId);
+ mHalNotifier.notifyHalOnPictureProfileChange(dbId, null);
}
- mMqManagerNotifier.notifyOnPictureProfileRemoved(
- mPictureProfileTempIdMap.getValue(dbId), toDelete,
- Binder.getCallingUid(), Binder.getCallingPid());
- mPictureProfileTempIdMap.remove(dbId);
- mHalNotifier.notifyHalOnPictureProfileChange(dbId, null);
}
}
}
@@ -520,12 +522,13 @@ public class MediaQualityService extends SystemService {
mMqManagerNotifier.notifyOnSoundProfileError(id,
SoundProfile.ERROR_INVALID_ARGUMENT,
Binder.getCallingUid(), Binder.getCallingPid());
+ } else {
+ mMqManagerNotifier.notifyOnSoundProfileRemoved(
+ mSoundProfileTempIdMap.getValue(dbId), toDelete,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ mSoundProfileTempIdMap.remove(dbId);
+ mHalNotifier.notifyHalOnSoundProfileChange(dbId, null);
}
- mMqManagerNotifier.notifyOnSoundProfileRemoved(
- mSoundProfileTempIdMap.getValue(dbId), toDelete,
- Binder.getCallingUid(), Binder.getCallingPid());
- mSoundProfileTempIdMap.remove(dbId);
- mHalNotifier.notifyHalOnSoundProfileChange(dbId, null);
}
}
}
@@ -684,24 +687,22 @@ public class MediaQualityService extends SystemService {
mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
}
- //TODO: need lock here?
@Override
public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
- UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
+ UserState userState = getOrCreateUserState(Binder.getCallingUid());
userState.mPictureProfileCallbackPidUidMap.put(callback,
Pair.create(callingPid, callingUid));
}
- //TODO: need lock here?
@Override
public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
- UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
+ UserState userState = getOrCreateUserState(Binder.getCallingUid());
userState.mSoundProfileCallbackPidUidMap.put(callback,
Pair.create(callingPid, callingUid));
}
@@ -792,7 +793,6 @@ public class MediaQualityService extends SystemService {
}
}
- //TODO: do I need a lock here?
@Override
public List<ParameterCapability> getParameterCapabilities(
List<String> names, UserHandle user) {
@@ -809,14 +809,20 @@ public class MediaQualityService extends SystemService {
private List<ParameterCapability> getListParameterCapability(ParamCapability[] caps) {
List<ParameterCapability> pcList = new ArrayList<>();
- for (ParamCapability pcHal : caps) {
- String name = MediaQualityUtils.getParameterName(pcHal.name);
- boolean isSupported = pcHal.isSupported;
- int type = pcHal.defaultValue == null ? 0 : pcHal.defaultValue.getTag() + 1;
- Bundle bundle = MediaQualityUtils.convertToCaps(type, pcHal.range);
- pcList.add(new ParameterCapability(name, isSupported, type, bundle));
+ if (caps != null) {
+ for (ParamCapability pcHal : caps) {
+ if (pcHal != null) {
+ String name = MediaQualityUtils.getParameterName(pcHal.name);
+ boolean isSupported = pcHal.isSupported;
+ int type = pcHal.defaultValue == null ? 0 : pcHal.defaultValue.getTag() + 1;
+ Bundle bundle = MediaQualityUtils.convertToCaps(type, pcHal.range);
+
+ pcList.add(new ParameterCapability(name, isSupported, type, bundle));
+ }
+ }
}
+
return pcList;
}
@@ -1055,7 +1061,7 @@ public class MediaQualityService extends SystemService {
synchronized (mPictureProfileLock) {
for (int i = 0; i < mUserStates.size(); i++) {
int userId = mUserStates.keyAt(i);
- UserState userState = getOrCreateUserStateLocked(userId);
+ UserState userState = getOrCreateUserState(userId);
userState.mPictureProfileCallbackPidUidMap.remove(callback);
}
}
@@ -1069,7 +1075,7 @@ public class MediaQualityService extends SystemService {
synchronized (mSoundProfileLock) {
for (int i = 0; i < mUserStates.size(); i++) {
int userId = mUserStates.keyAt(i);
- UserState userState = getOrCreateUserStateLocked(userId);
+ UserState userState = getOrCreateUserState(userId);
userState.mSoundProfileCallbackPidUidMap.remove(callback);
}
}
@@ -1095,25 +1101,27 @@ public class MediaQualityService extends SystemService {
}
}
- //TODO: used by both picture and sound. can i add both locks?
- private UserState getOrCreateUserStateLocked(int userId) {
- UserState userState = getUserStateLocked(userId);
+ @GuardedBy("mUserStateLock")
+ private UserState getOrCreateUserState(int userId) {
+ UserState userState = getUserState(userId);
if (userState == null) {
userState = new UserState(mContext, userId);
- mUserStates.put(userId, userState);
+ synchronized (mUserStateLock) {
+ mUserStates.put(userId, userState);
+ }
}
return userState;
}
- //TODO: used by both picture and sound. can i add both locks?
- private UserState getUserStateLocked(int userId) {
- return mUserStates.get(userId);
+ @GuardedBy("mUserStateLock")
+ private UserState getUserState(int userId) {
+ synchronized (mUserStateLock) {
+ return mUserStates.get(userId);
+ }
}
private final class MqDatabaseUtils {
- MediaQualityDbHelper mMediaQualityDbHelper;
-
private PictureProfile getPictureProfile(Long dbId) {
String selection = BaseParameters.PARAMETER_ID + " = ?";
String[] selectionArguments = {Long.toString(dbId)};
@@ -1205,8 +1213,7 @@ public class MediaQualityService extends SystemService {
/*groupBy=*/ null, /*having=*/ null, /*orderBy=*/ null);
}
- private MqDatabaseUtils(Context context) {
- mMediaQualityDbHelper = new MediaQualityDbHelper(context);
+ private MqDatabaseUtils() {
}
}
@@ -1264,7 +1271,7 @@ public class MediaQualityService extends SystemService {
private void notifyPictureProfileHelper(int mode, String profileId,
PictureProfile profile, Integer errorCode,
List<ParameterCapability> paramCaps, int uid, int pid) {
- UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
+ UserState userState = getOrCreateUserState(UserHandle.USER_SYSTEM);
int n = userState.mPictureProfileCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
@@ -1349,7 +1356,7 @@ public class MediaQualityService extends SystemService {
private void notifySoundProfileHelper(int mode, String profileId,
SoundProfile profile, Integer errorCode,
List<ParameterCapability> paramCaps, int uid, int pid) {
- UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
+ UserState userState = getOrCreateUserState(UserHandle.USER_SYSTEM);
int n = userState.mSoundProfileCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
@@ -1404,11 +1411,13 @@ public class MediaQualityService extends SystemService {
private void notifyHalOnPictureProfileChange(Long dbId, PersistableBundle params) {
// TODO: only notify HAL when the profile is active / being used
- try {
- mPpChangedListener.onPictureProfileChanged(convertToHalPictureProfile(dbId,
- params));
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to notify HAL on picture profile change.", e);
+ if (mPpChangedListener != null) {
+ try {
+ mPpChangedListener.onPictureProfileChanged(convertToHalPictureProfile(dbId,
+ params));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify HAL on picture profile change.", e);
+ }
}
}
@@ -1429,10 +1438,13 @@ public class MediaQualityService extends SystemService {
private void notifyHalOnSoundProfileChange(Long dbId, PersistableBundle params) {
// TODO: only notify HAL when the profile is active / being used
- try {
- mSpChangedListener.onSoundProfileChanged(convertToHalSoundProfile(dbId, params));
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to notify HAL on sound profile change.", e);
+ if (mSpChangedListener != null) {
+ try {
+ mSpChangedListener
+ .onSoundProfileChanged(convertToHalSoundProfile(dbId, params));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify HAL on sound profile change.", e);
+ }
}
}
@@ -1488,9 +1500,6 @@ public class MediaQualityService extends SystemService {
private final class PictureProfileAdjustmentListenerImpl extends
IPictureProfileAdjustmentListener.Stub {
- MqDatabaseUtils mMqDatabaseUtils;
- MqManagerNotifier mMqManagerNotifier;
- HalNotifier mHalNotifier;
@Override
public void onPictureProfileAdjusted(
@@ -1542,18 +1551,13 @@ public class MediaQualityService extends SystemService {
return null;
}
- private PictureProfileAdjustmentListenerImpl(Context context) {
- mMqDatabaseUtils = new MqDatabaseUtils(context);
- mMqManagerNotifier = new MqManagerNotifier();
- mHalNotifier = new HalNotifier();
+ private PictureProfileAdjustmentListenerImpl() {
+
}
}
private final class SoundProfileAdjustmentListenerImpl extends
ISoundProfileAdjustmentListener.Stub {
- MqDatabaseUtils mMqDatabaseUtils;
- MqManagerNotifier mMqManagerNotifier;
- HalNotifier mHalNotifier;
@Override
public void onSoundProfileAdjusted(
@@ -1598,10 +1602,8 @@ public class MediaQualityService extends SystemService {
return null;
}
- private SoundProfileAdjustmentListenerImpl(Context context) {
- mMqDatabaseUtils = new MqDatabaseUtils(context);
- mMqManagerNotifier = new MqManagerNotifier();
- mHalNotifier = new HalNotifier();
+ private SoundProfileAdjustmentListenerImpl() {
+
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b0ef80793cd7..62e26e189a35 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -25,6 +25,8 @@ import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
+import static com.android.server.notification.Flags.FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER;
+import static com.android.server.notification.Flags.managedServicesConcurrentMultiuser;
import static com.android.server.notification.NotificationManagerService.privateSpaceFlagsEnabled;
import android.annotation.FlaggedApi;
@@ -75,7 +77,9 @@ import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import org.xmlpull.v1.XmlPullParser;
@@ -134,6 +138,7 @@ abstract public class ManagedServices {
private final UserProfiles mUserProfiles;
protected final IPackageManager mPm;
protected final UserManager mUm;
+ protected final UserManagerInternal mUmInternal;
private final Config mConfig;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -157,12 +162,17 @@ abstract public class ManagedServices {
protected final ArraySet<String> mDefaultPackages = new ArraySet<>();
// lists the component names of all enabled (and therefore potentially connected)
- // app services for current profiles.
+ // app services for each user. This is intended to support a concurrent multi-user environment.
+ // key value is the resolved userId.
@GuardedBy("mMutex")
- private final ArraySet<ComponentName> mEnabledServicesForCurrentProfiles = new ArraySet<>();
- // Just the packages from mEnabledServicesForCurrentProfiles
+ private final SparseArray<ArraySet<ComponentName>> mEnabledServicesByUser =
+ new SparseArray<>();
+ // Just the packages from mEnabledServicesByUser
+ // This is intended to support a concurrent multi-user environment.
+ // key value is the resolved userId.
@GuardedBy("mMutex")
- private final ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
+ private final SparseArray<ArraySet<String>> mEnabledServicesPackageNamesByUser =
+ new SparseArray<>();
// Per user id, list of enabled packages that have nevertheless asked not to be run
@GuardedBy("mSnoozing")
private final SparseSetArray<ComponentName> mSnoozing = new SparseSetArray<>();
@@ -195,6 +205,10 @@ abstract public class ManagedServices {
mConfig = getConfig();
mApprovalLevel = APPROVAL_BY_COMPONENT;
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mUmInternal = LocalServices.getService(UserManagerInternal.class);
+ // Initialize for the current user.
+ mEnabledServicesByUser.put(UserHandle.USER_CURRENT, new ArraySet<>());
+ mEnabledServicesPackageNamesByUser.put(UserHandle.USER_CURRENT, new ArraySet<>());
}
abstract protected Config getConfig();
@@ -383,11 +397,30 @@ abstract public class ManagedServices {
}
synchronized (mMutex) {
- pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
- + ") enabled for current profiles:");
- for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
- if (filter != null && !filter.matches(cmpt)) continue;
- pw.println(" " + cmpt);
+ if (managedServicesConcurrentMultiuser()) {
+ for (int i = 0; i < mEnabledServicesByUser.size(); i++) {
+ final int userId = mEnabledServicesByUser.keyAt(i);
+ final ArraySet<ComponentName> componentNames =
+ mEnabledServicesByUser.get(userId);
+ String userString = userId == UserHandle.USER_CURRENT
+ ? "current profiles" : "user " + Integer.toString(userId);
+ pw.println(" All " + getCaption() + "s (" + componentNames.size()
+ + ") enabled for " + userString + ":");
+ for (ComponentName cmpt : componentNames) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ pw.println(" " + cmpt);
+ }
+ }
+ } else {
+ final ArraySet<ComponentName> enabledServicesForCurrentProfiles =
+ mEnabledServicesByUser.get(UserHandle.USER_CURRENT);
+ pw.println(" All " + getCaption() + "s ("
+ + enabledServicesForCurrentProfiles.size()
+ + ") enabled for current profiles:");
+ for (ComponentName cmpt : enabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ pw.println(" " + cmpt);
+ }
}
pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
@@ -442,11 +475,24 @@ abstract public class ManagedServices {
}
}
-
synchronized (mMutex) {
- for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
- if (filter != null && !filter.matches(cmpt)) continue;
- cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
+ if (managedServicesConcurrentMultiuser()) {
+ for (int i = 0; i < mEnabledServicesByUser.size(); i++) {
+ final int userId = mEnabledServicesByUser.keyAt(i);
+ final ArraySet<ComponentName> componentNames =
+ mEnabledServicesByUser.get(userId);
+ for (ComponentName cmpt : componentNames) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
+ }
+ }
+ } else {
+ final ArraySet<ComponentName> enabledServicesForCurrentProfiles =
+ mEnabledServicesByUser.get(UserHandle.USER_CURRENT);
+ for (ComponentName cmpt : enabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
+ }
}
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
@@ -841,9 +887,31 @@ abstract public class ManagedServices {
}
}
+ /** convenience method for looking in mEnabledServicesPackageNamesByUser
+ * for UserHandle.USER_CURRENT.
+ * This is a legacy API. When FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER becomes
+ * trunk stable, this API should be deprecated. Additionally, when this method
+ * is deprecated, the unit tests written using this method should also be revised.
+ *
+ * @param pkg target package name
+ * @return boolean value that indicates whether it is enabled for the current profiles
+ */
protected boolean isComponentEnabledForPackage(String pkg) {
+ return isComponentEnabledForPackage(pkg, UserHandle.USER_CURRENT);
+ }
+
+ /** convenience method for looking in mEnabledServicesPackageNamesByUser
+ *
+ * @param pkg target package name
+ * @param userId the id of the target user
+ * @return boolean value that indicates whether it is enabled for the target user
+ */
+ @FlaggedApi(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ protected boolean isComponentEnabledForPackage(String pkg, int userId) {
synchronized (mMutex) {
- return mEnabledServicesPackageNames.contains(pkg);
+ ArraySet<String> enabledServicesPackageNames =
+ mEnabledServicesPackageNamesByUser.get(resolveUserId(userId));
+ return enabledServicesPackageNames != null && enabledServicesPackageNames.contains(pkg);
}
}
@@ -1016,9 +1084,14 @@ abstract public class ManagedServices {
public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
if (DEBUG) {
synchronized (mMutex) {
+ int resolvedUserId = (managedServicesConcurrentMultiuser()
+ && (uidList != null && uidList.length > 0))
+ ? resolveUserId(UserHandle.getUserId(uidList[0]))
+ : UserHandle.USER_CURRENT;
Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
- + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+ + " mEnabledServicesPackageNames="
+ + mEnabledServicesPackageNamesByUser.get(resolvedUserId));
}
}
@@ -1034,11 +1107,18 @@ abstract public class ManagedServices {
}
}
for (String pkgName : pkgList) {
- if (isComponentEnabledForPackage(pkgName)) {
- anyServicesInvolved = true;
+ if (!managedServicesConcurrentMultiuser()) {
+ if (isComponentEnabledForPackage(pkgName)) {
+ anyServicesInvolved = true;
+ }
}
if (uidList != null && uidList.length > 0) {
for (int uid : uidList) {
+ if (managedServicesConcurrentMultiuser()) {
+ if (isComponentEnabledForPackage(pkgName, UserHandle.getUserId(uid))) {
+ anyServicesInvolved = true;
+ }
+ }
if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) {
anyServicesInvolved = true;
trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid));
@@ -1065,6 +1145,36 @@ abstract public class ManagedServices {
unbindUserServices(user);
}
+ /**
+ * Call this method when a user is stopped
+ *
+ * @param user the id of the stopped user
+ */
+ public void onUserStopped(int user) {
+ if (!managedServicesConcurrentMultiuser()) {
+ return;
+ }
+ boolean hasAny = false;
+ synchronized (mMutex) {
+ if (mEnabledServicesByUser.contains(user)
+ && mEnabledServicesPackageNamesByUser.contains(user)) {
+ // Through the ManagedServices.resolveUserId,
+ // we resolve UserHandle.USER_CURRENT as the key for users
+ // other than the visible background user.
+ // Therefore, the user IDs that exist as keys for each member variable
+ // correspond to the visible background user.
+ // We need to unbind services of the stopped visible background user.
+ mEnabledServicesByUser.remove(user);
+ mEnabledServicesPackageNamesByUser.remove(user);
+ hasAny = true;
+ }
+ }
+ if (hasAny) {
+ Slog.i(TAG, "Removing approved services for stopped user " + user);
+ unbindUserServices(user);
+ }
+ }
+
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
unbindOtherUserServices(user);
@@ -1386,19 +1496,42 @@ abstract public class ManagedServices {
protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
final IntArray activeUsers,
SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
- mEnabledServicesForCurrentProfiles.clear();
- mEnabledServicesPackageNames.clear();
final int nUserIds = activeUsers.size();
-
+ if (managedServicesConcurrentMultiuser()) {
+ for (int i = 0; i < nUserIds; ++i) {
+ final int resolvedUserId = resolveUserId(activeUsers.get(i));
+ if (mEnabledServicesByUser.get(resolvedUserId) != null) {
+ mEnabledServicesByUser.get(resolvedUserId).clear();
+ }
+ if (mEnabledServicesPackageNamesByUser.get(resolvedUserId) != null) {
+ mEnabledServicesPackageNamesByUser.get(resolvedUserId).clear();
+ }
+ }
+ } else {
+ mEnabledServicesByUser.get(UserHandle.USER_CURRENT).clear();
+ mEnabledServicesPackageNamesByUser.get(UserHandle.USER_CURRENT).clear();
+ }
for (int i = 0; i < nUserIds; ++i) {
- // decode the list of components
final int userId = activeUsers.get(i);
+ // decode the list of components
final ArraySet<ComponentName> userComponents = approvedComponentsByUser.get(userId);
if (null == userComponents) {
componentsToBind.put(userId, new ArraySet<>());
continue;
}
+ final int resolvedUserId = managedServicesConcurrentMultiuser()
+ ? resolveUserId(userId)
+ : UserHandle.USER_CURRENT;
+ ArraySet<ComponentName> enabledServices =
+ mEnabledServicesByUser.contains(resolvedUserId)
+ ? mEnabledServicesByUser.get(resolvedUserId)
+ : new ArraySet<>();
+ ArraySet<String> enabledServicesPackageName =
+ mEnabledServicesPackageNamesByUser.contains(resolvedUserId)
+ ? mEnabledServicesPackageNamesByUser.get(resolvedUserId)
+ : new ArraySet<>();
+
final Set<ComponentName> add = new HashSet<>(userComponents);
synchronized (mSnoozing) {
ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
@@ -1409,12 +1542,12 @@ abstract public class ManagedServices {
componentsToBind.put(userId, add);
- mEnabledServicesForCurrentProfiles.addAll(userComponents);
-
+ enabledServices.addAll(userComponents);
for (int j = 0; j < userComponents.size(); j++) {
- final ComponentName component = userComponents.valueAt(j);
- mEnabledServicesPackageNames.add(component.getPackageName());
+ enabledServicesPackageName.add(userComponents.valueAt(j).getPackageName());
}
+ mEnabledServicesByUser.put(resolvedUserId, enabledServices);
+ mEnabledServicesPackageNamesByUser.put(resolvedUserId, enabledServicesPackageName);
}
}
@@ -1453,13 +1586,9 @@ abstract public class ManagedServices {
*/
protected void rebindServices(boolean forceRebind, int userToRebind) {
if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind);
- IntArray userIds = mUserProfiles.getCurrentProfileIds();
boolean rebindAllCurrentUsers = mUserProfiles.isProfileUser(userToRebind, mContext)
&& allowRebindForParentUser();
- if (userToRebind != USER_ALL && !rebindAllCurrentUsers) {
- userIds = new IntArray(1);
- userIds.add(userToRebind);
- }
+ IntArray userIds = getUserIdsForRebindServices(userToRebind, rebindAllCurrentUsers);
final SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
@@ -1483,6 +1612,23 @@ abstract public class ManagedServices {
bindToServices(componentsToBind);
}
+ private IntArray getUserIdsForRebindServices(int userToRebind, boolean rebindAllCurrentUsers) {
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
+ if (userToRebind != USER_ALL && !rebindAllCurrentUsers) {
+ userIds = new IntArray(1);
+ userIds.add(userToRebind);
+ } else if (managedServicesConcurrentMultiuser()
+ && userToRebind == USER_ALL) {
+ for (UserInfo user : mUm.getUsers()) {
+ if (mUmInternal.isVisibleBackgroundFullUser(user.id)
+ && !userIds.contains(user.id)) {
+ userIds.add(user.id);
+ }
+ }
+ }
+ return userIds;
+ }
+
/**
* Called when user switched to unbind all services from other users.
*/
@@ -1506,7 +1652,11 @@ abstract public class ManagedServices {
synchronized (mMutex) {
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
- if ((allExceptUser && (info.userid != user))
+ // User switching is the event for the forground user.
+ // It should not affect the service of the visible background user.
+ if ((allExceptUser && (info.userid != user)
+ && !(managedServicesConcurrentMultiuser()
+ && info.isVisibleBackgroundUserService))
|| (!allExceptUser && (info.userid == user))) {
Set<ComponentName> toUnbind =
componentsToUnbind.get(info.userid, new ArraySet<>());
@@ -1861,6 +2011,29 @@ abstract public class ManagedServices {
}
/**
+ * This method returns the mapped id for the incoming user id
+ * If the incoming id was not the id of the visible background user, it returns USER_CURRENT.
+ * In the other cases, it returns the same value as the input.
+ *
+ * @param userId the id of the user
+ * @return the user id if it is a visible background user, otherwise
+ * {@link UserHandle#USER_CURRENT}
+ */
+ @FlaggedApi(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ @VisibleForTesting
+ public int resolveUserId(int userId) {
+ if (managedServicesConcurrentMultiuser()) {
+ if (mUmInternal.isVisibleBackgroundFullUser(userId)) {
+ // The dataset of the visible background user should be managed independently.
+ return userId;
+ }
+ }
+ // The data of current user and its profile users need to be managed
+ // in a dataset as before.
+ return UserHandle.USER_CURRENT;
+ }
+
+ /**
* Returns true if services in the parent user should be rebound
* when rebindServices is called with a profile userId.
* Must be false for NotificationAssistants.
@@ -1878,6 +2051,8 @@ abstract public class ManagedServices {
public int targetSdkVersion;
public Pair<ComponentName, Integer> mKey;
public int uid;
+ @FlaggedApi(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public boolean isVisibleBackgroundUserService;
public ManagedServiceInfo(IInterface service, ComponentName component,
int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion,
@@ -1889,6 +2064,10 @@ abstract public class ManagedServices {
this.connection = connection;
this.targetSdkVersion = targetSdkVersion;
this.uid = uid;
+ if (managedServicesConcurrentMultiuser()) {
+ this.isVisibleBackgroundUserService = LocalServices
+ .getService(UserManagerInternal.class).isVisibleBackgroundFullUser(userid);
+ }
mKey = Pair.create(component, userid);
}
@@ -1937,19 +2116,28 @@ abstract public class ManagedServices {
}
public boolean isSameUser(int userId) {
- if (!isEnabledForCurrentProfiles()) {
+ if (!isEnabledForUser()) {
return false;
}
return userId == USER_ALL || userId == this.userid;
}
public boolean enabledAndUserMatches(int nid) {
- if (!isEnabledForCurrentProfiles()) {
+ if (!isEnabledForUser()) {
return false;
}
if (this.userid == USER_ALL) return true;
if (this.isSystem) return true;
if (nid == USER_ALL || nid == this.userid) return true;
+ if (managedServicesConcurrentMultiuser()
+ && mUmInternal.getProfileParentId(nid)
+ != mUmInternal.getProfileParentId(this.userid)) {
+ // If the profile parent IDs do not match each other,
+ // it is determined that the users do not match.
+ // This situation may occur when comparing the current user's ID
+ // with the visible background user's ID.
+ return false;
+ }
return supportsProfiles()
&& mUserProfiles.isCurrentProfile(nid)
&& isPermittedForProfile(nid);
@@ -1969,12 +2157,21 @@ abstract public class ManagedServices {
removeServiceImpl(this.service, this.userid);
}
- /** convenience method for looking in mEnabledServicesForCurrentProfiles */
- public boolean isEnabledForCurrentProfiles() {
+ /**
+ * convenience method for looking in mEnabledServicesByUser.
+ * If FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER is disabled, this manages the data using
+ * only UserHandle.USER_CURRENT as the key, in order to behave the same as the legacy logic.
+ */
+ public boolean isEnabledForUser() {
if (this.isSystem) return true;
if (this.connection == null) return false;
synchronized (mMutex) {
- return mEnabledServicesForCurrentProfiles.contains(this.component);
+ int resolvedUserId = managedServicesConcurrentMultiuser()
+ ? resolveUserId(this.userid)
+ : UserHandle.USER_CURRENT;
+ ArraySet<ComponentName> enabledServices =
+ mEnabledServicesByUser.get(resolvedUserId);
+ return enabledServices != null && enabledServices.contains(this.component);
}
}
@@ -2017,10 +2214,30 @@ abstract public class ManagedServices {
}
}
- /** convenience method for looking in mEnabledServicesForCurrentProfiles */
+ /** convenience method for looking in mEnabledServicesByUser for UserHandle.USER_CURRENT.
+ * This is a legacy API. When FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER becomes
+ * trunk stable, this API should be deprecated. Additionally, when this method
+ * is deprecated, the unit tests written using this method should also be revised.
+ *
+ * @param component target component name
+ * @return boolean value that indicates whether it is enabled for the current profiles
+ */
public boolean isComponentEnabledForCurrentProfiles(ComponentName component) {
+ return isComponentEnabledForUser(component, UserHandle.USER_CURRENT);
+ }
+
+ /** convenience method for looking in mEnabledServicesForUser
+ *
+ * @param component target component name
+ * @param userId the id of the target user
+ * @return boolean value that indicates whether it is enabled for the target user
+ */
+ @FlaggedApi(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public boolean isComponentEnabledForUser(ComponentName component, int userId) {
synchronized (mMutex) {
- return mEnabledServicesForCurrentProfiles.contains(component);
+ ArraySet<ComponentName> enabledServicesForUser =
+ mEnabledServicesByUser.get(resolveUserId(userId));
+ return enabledServicesForUser != null && enabledServicesForUser.contains(component);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 340afb776405..6fddfb5f90a7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -173,6 +173,7 @@ import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
import static com.android.server.notification.Flags.expireBitmaps;
+import static com.android.server.notification.Flags.managedServicesConcurrentMultiuser;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
@@ -1207,7 +1208,7 @@ public class NotificationManagerService extends SystemService {
}
mAssistants.resetDefaultAssistantsIfNecessary();
- mPreferencesHelper.syncChannelsBypassingDnd();
+ mPreferencesHelper.syncHasPriorityChannels();
}
@VisibleForTesting
@@ -2323,6 +2324,9 @@ public class NotificationManagerService extends SystemService {
if (userHandle >= 0) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle,
REASON_USER_STOPPED);
+ mConditionProviders.onUserStopped(userHandle);
+ mListeners.onUserStopped(userHandle);
+ mAssistants.onUserStopped(userHandle);
}
} else if (
isProfileUnavailable(action)) {
@@ -2343,7 +2347,7 @@ public class NotificationManagerService extends SystemService {
mConditionProviders.onUserSwitched(userId);
mListeners.onUserSwitched(userId);
mZenModeHelper.onUserSwitched(userId);
- mPreferencesHelper.syncChannelsBypassingDnd();
+ mPreferencesHelper.syncHasPriorityChannels();
}
// assistant is the only thing that cares about managed profiles specifically
mAssistants.onUserSwitched(userId);
@@ -2367,7 +2371,7 @@ public class NotificationManagerService extends SystemService {
mConditionProviders.onUserRemoved(userId);
mAssistants.onUserRemoved(userId);
mHistoryManager.onUserRemoved(userId);
- mPreferencesHelper.syncChannelsBypassingDnd();
+ mPreferencesHelper.syncHasPriorityChannels();
handleSavePolicyFile();
} else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
@@ -2376,9 +2380,6 @@ public class NotificationManagerService extends SystemService {
if (!mUserProfiles.isProfileUser(userId, context)) {
mConditionProviders.onUserUnlocked(userId);
mListeners.onUserUnlocked(userId);
- if (!android.app.Flags.modesApi()) {
- mZenModeHelper.onUserUnlocked(userId);
- }
}
}
}
@@ -2767,9 +2768,7 @@ public class NotificationManagerService extends SystemService {
void onPolicyChanged(Policy newPolicy) {
Binder.withCleanCallingIdentity(() -> {
Intent intent = new Intent(ACTION_NOTIFICATION_POLICY_CHANGED);
- if (android.app.Flags.modesApi()) {
- intent.putExtra(EXTRA_NOTIFICATION_POLICY, newPolicy);
- }
+ intent.putExtra(EXTRA_NOTIFICATION_POLICY, newPolicy);
sendRegisteredOnlyBroadcast(intent);
mRankingHandler.requestSort();
});
@@ -2778,11 +2777,10 @@ public class NotificationManagerService extends SystemService {
@Override
void onConsolidatedPolicyChanged(Policy newConsolidatedPolicy) {
Binder.withCleanCallingIdentity(() -> {
- if (android.app.Flags.modesApi()) {
- Intent intent = new Intent(ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED);
- intent.putExtra(EXTRA_NOTIFICATION_POLICY, newConsolidatedPolicy);
- sendRegisteredOnlyBroadcast(intent);
- }
+ Intent intent = new Intent(ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED);
+ intent.putExtra(EXTRA_NOTIFICATION_POLICY, newConsolidatedPolicy);
+ sendRegisteredOnlyBroadcast(intent);
+
mRankingHandler.requestSort();
});
}
@@ -3368,7 +3366,7 @@ public class NotificationManagerService extends SystemService {
migrateDefaultNAS();
maybeShowInitialReviewPermissionsNotification();
- if (android.app.Flags.modesApi() && !mZenModeHelper.hasDeviceEffectsApplier()) {
+ if (!mZenModeHelper.hasDeviceEffectsApplier()) {
// Cannot be done earlier, as some services aren't ready until this point.
mZenModeHelper.setDeviceEffectsApplier(
new DefaultDeviceEffectsApplier(getContext()));
@@ -3446,7 +3444,7 @@ public class NotificationManagerService extends SystemService {
mConditionProviders.onUserSwitched(userId);
mListeners.onUserSwitched(userId);
mZenModeHelper.onUserSwitched(userId);
- mPreferencesHelper.syncChannelsBypassingDnd();
+ mPreferencesHelper.syncHasPriorityChannels();
}
// assistant is the only thing that cares about managed profiles specifically
mAssistants.onUserSwitched(userId);
@@ -5236,11 +5234,8 @@ public class NotificationManagerService extends SystemService {
@Override
public boolean areChannelsBypassingDnd() {
- if (android.app.Flags.modesApi()) {
- return mZenModeHelper.getConsolidatedNotificationPolicy().allowPriorityChannels()
- && mPreferencesHelper.areChannelsBypassingDnd();
- }
- return mPreferencesHelper.areChannelsBypassingDnd();
+ return mZenModeHelper.getConsolidatedNotificationPolicy().allowPriorityChannels()
+ && mPreferencesHelper.hasPriorityChannels();
}
@Override
@@ -5730,12 +5725,13 @@ public class NotificationManagerService extends SystemService {
public void requestBindListener(ComponentName component) {
checkCallerIsSystemOrSameApp(component.getPackageName());
int uid = Binder.getCallingUid();
+ int userId = UserHandle.getUserId(uid);
final long identity = Binder.clearCallingIdentity();
try {
- ManagedServices manager =
- mAssistants.isComponentEnabledForCurrentProfiles(component)
- ? mAssistants
- : mListeners;
+ boolean isAssistantEnabled = managedServicesConcurrentMultiuser()
+ ? mAssistants.isComponentEnabledForUser(component, userId)
+ : mAssistants.isComponentEnabledForCurrentProfiles(component);
+ ManagedServices manager = isAssistantEnabled ? mAssistants : mListeners;
manager.setComponentState(component, UserHandle.getUserId(uid), true);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -5762,16 +5758,16 @@ public class NotificationManagerService extends SystemService {
public void requestUnbindListenerComponent(ComponentName component) {
checkCallerIsSameApp(component.getPackageName());
int uid = Binder.getCallingUid();
+ int userId = UserHandle.getUserId(uid);
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationLock) {
- ManagedServices manager =
- mAssistants.isComponentEnabledForCurrentProfiles(component)
- ? mAssistants
- : mListeners;
- if (manager.isPackageOrComponentAllowed(component.flattenToString(),
- UserHandle.getUserId(uid))) {
- manager.setComponentState(component, UserHandle.getUserId(uid), false);
+ boolean isAssistantEnabled = managedServicesConcurrentMultiuser()
+ ? mAssistants.isComponentEnabledForUser(component, userId)
+ : mAssistants.isComponentEnabledForCurrentProfiles(component);
+ ManagedServices manager = isAssistantEnabled ? mAssistants : mListeners;
+ if (manager.isPackageOrComponentAllowed(component.flattenToString(), userId)) {
+ manager.setComponentState(component, userId, false);
}
}
} finally {
@@ -6092,43 +6088,27 @@ public class NotificationManagerService extends SystemService {
@Override
public void requestInterruptionFilterFromListener(INotificationListener token,
int interruptionFilter) throws RemoteException {
- if (android.app.Flags.modesApi()) {
- final int callingUid = Binder.getCallingUid();
- ManagedServiceInfo info;
- synchronized (mNotificationLock) {
- info = mListeners.checkServiceTokenLocked(token);
- }
+ final int callingUid = Binder.getCallingUid();
+ ManagedServiceInfo info;
+ synchronized (mNotificationLock) {
+ info = mListeners.checkServiceTokenLocked(token);
+ }
- final int zenMode = zenModeFromInterruptionFilter(interruptionFilter, -1);
- if (zenMode == -1) return;
+ final int zenMode = zenModeFromInterruptionFilter(interruptionFilter, -1);
+ if (zenMode == -1) return;
- UserHandle zenUser = getCallingZenUser();
- if (!canManageGlobalZenPolicy(info.component.getPackageName(), callingUid)) {
- mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(
- zenUser, info.component.getPackageName(), callingUid, zenMode);
- } else {
- int origin = computeZenOrigin(/* fromUser= */ false);
- Binder.withCleanCallingIdentity(() -> {
- mZenModeHelper.setManualZenMode(zenUser, zenMode, /* conditionId= */ null,
- origin, "listener:" + info.component.flattenToShortString(),
- /* caller= */ info.component.getPackageName(),
- callingUid);
- });
- }
+ UserHandle zenUser = getCallingZenUser();
+ if (!canManageGlobalZenPolicy(info.component.getPackageName(), callingUid)) {
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(
+ zenUser, info.component.getPackageName(), callingUid, zenMode);
} else {
- final int callingUid = Binder.getCallingUid();
- final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mNotificationLock) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- mZenModeHelper.requestFromListener(info.component, interruptionFilter,
- callingUid, isSystemOrSystemUi);
- updateInterruptionFilterLocked();
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ int origin = computeZenOrigin(/* fromUser= */ false);
+ Binder.withCleanCallingIdentity(() -> {
+ mZenModeHelper.setManualZenMode(zenUser, zenMode, /* conditionId= */ null,
+ origin, "listener:" + info.component.flattenToShortString(),
+ /* caller= */ info.component.getPackageName(),
+ callingUid);
+ });
}
}
@@ -6177,19 +6157,8 @@ public class NotificationManagerService extends SystemService {
}
}
- // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
- @Override
- public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
- int callingUid = Binder.getCallingUid();
- enforcePolicyAccess(callingUid, "getZenRules");
- return mZenModeHelper.getZenRules(getCallingZenUser(), callingUid);
- }
-
@Override
public Map<String, AutomaticZenRule> getAutomaticZenRules() {
- if (!android.app.Flags.modesApi()) {
- throw new IllegalStateException("getAutomaticZenRules called with flag off!");
- }
int callingUid = Binder.getCallingUid();
enforcePolicyAccess(callingUid, "getAutomaticZenRules");
return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid);
@@ -6260,50 +6229,40 @@ public class NotificationManagerService extends SystemService {
// Implicit rules have no ConditionProvider or Activity. We allow the user to customize
// them (via Settings), but not the owner app. Should the app want to start using it as
// a "normal" rule, it must provide a CP/ConfigActivity too.
- if (android.app.Flags.modesApi()) {
- boolean isImplicitRuleUpdateFromSystem = updateId != null
- && ZenModeConfig.isImplicitRuleId(updateId)
- && isCallerSystemOrSystemUi();
- if (!isImplicitRuleUpdateFromSystem
- && rule.getOwner() == null
- && rule.getConfigurationActivity() == null) {
- throw new NullPointerException(
- "Rule must have a ConditionProviderService and/or configuration "
- + "activity");
- }
- } else {
- if (rule.getOwner() == null && rule.getConfigurationActivity() == null) {
- throw new NullPointerException(
- "Rule must have a ConditionProviderService and/or configuration "
- + "activity");
- }
+ boolean isImplicitRuleUpdateFromSystem = updateId != null
+ && ZenModeConfig.isImplicitRuleId(updateId)
+ && isCallerSystemOrSystemUi();
+ if (!isImplicitRuleUpdateFromSystem
+ && rule.getOwner() == null
+ && rule.getConfigurationActivity() == null) {
+ throw new NullPointerException(
+ "Rule must have a ConditionProviderService and/or configuration "
+ + "activity");
}
Objects.requireNonNull(rule.getConditionId(), "ConditionId is null");
- if (android.app.Flags.modesApi()) {
- if (isCallerSystemOrSystemUi()) {
- return; // System callers can use any type.
- }
- int uid = Binder.getCallingUid();
- int userId = UserHandle.getUserId(uid);
+ if (isCallerSystemOrSystemUi()) {
+ return; // System callers can use any type.
+ }
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getUserId(uid);
- if (rule.getType() == AutomaticZenRule.TYPE_MANAGED) {
- boolean isDeviceOwner = Binder.withCleanCallingIdentity(
- () -> mDpm.isActiveDeviceOwner(uid));
- if (!isDeviceOwner) {
- throw new IllegalArgumentException(
- "Only Device Owners can use AutomaticZenRules with TYPE_MANAGED");
- }
- } else if (rule.getType() == AutomaticZenRule.TYPE_BEDTIME) {
- String wellbeingPackage = getContext().getResources().getString(
- com.android.internal.R.string.config_systemWellbeing);
- boolean isCallerWellbeing = !TextUtils.isEmpty(wellbeingPackage)
- && mPackageManagerInternal.isSameApp(wellbeingPackage, uid, userId);
- if (!isCallerWellbeing) {
- throw new IllegalArgumentException(
- "Only the 'Wellbeing' package can use AutomaticZenRules with "
- + "TYPE_BEDTIME");
- }
+ if (rule.getType() == AutomaticZenRule.TYPE_MANAGED) {
+ boolean isDeviceOwner = Binder.withCleanCallingIdentity(
+ () -> mDpm.isActiveDeviceOwner(uid));
+ if (!isDeviceOwner) {
+ throw new IllegalArgumentException(
+ "Only Device Owners can use AutomaticZenRules with TYPE_MANAGED");
+ }
+ } else if (rule.getType() == AutomaticZenRule.TYPE_BEDTIME) {
+ String wellbeingPackage = getContext().getResources().getString(
+ com.android.internal.R.string.config_systemWellbeing);
+ boolean isCallerWellbeing = !TextUtils.isEmpty(wellbeingPackage)
+ && mPackageManagerInternal.isSameApp(wellbeingPackage, uid, userId);
+ if (!isCallerWellbeing) {
+ throw new IllegalArgumentException(
+ "Only the 'Wellbeing' package can use AutomaticZenRules with "
+ + "TYPE_BEDTIME");
}
}
}
@@ -6386,9 +6345,7 @@ public class NotificationManagerService extends SystemService {
@ZenModeConfig.ConfigOrigin
private int computeZenOrigin(boolean fromUser) {
- // "fromUser" is introduced with MODES_API, so only consider it in that case.
- // (Non-MODES_API behavior should also not depend at all on ORIGIN_USER_IN_X).
- if (android.app.Flags.modesApi() && fromUser) {
+ if (fromUser) {
if (isCallerSystemOrSystemUi()) {
return ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
} else {
@@ -6402,9 +6359,7 @@ public class NotificationManagerService extends SystemService {
}
private void enforceUserOriginOnlyFromSystem(boolean fromUser, String method) {
- if (android.app.Flags.modesApi()
- && fromUser
- && !isCallerSystemOrSystemUiOrShell()) {
+ if (fromUser && !isCallerSystemOrSystemUiOrShell()) {
throw new SecurityException(TextUtils.formatSimple(
"Calling %s with fromUser == true is only allowed for system", method));
}
@@ -6419,7 +6374,7 @@ public class NotificationManagerService extends SystemService {
enforceUserOriginOnlyFromSystem(fromUser, "setInterruptionFilter");
UserHandle zenUser = getCallingZenUser();
- if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+ if (!canManageGlobalZenPolicy(pkg, callingUid)) {
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(zenUser, pkg, callingUid, zen);
return;
}
@@ -6549,6 +6504,13 @@ public class NotificationManagerService extends SystemService {
} catch (NameNotFoundException e) {
return false;
}
+ if (managedServicesConcurrentMultiuser()) {
+ return checkPackagePolicyAccess(pkg)
+ || mListeners.isComponentEnabledForPackage(pkg,
+ UserHandle.getCallingUserId())
+ || (mDpm != null
+ && (mDpm.isActiveProfileOwner(uid) || mDpm.isActiveDeviceOwner(uid)));
+ }
//TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
return checkPackagePolicyAccess(pkg)
|| mListeners.isComponentEnabledForPackage(pkg)
@@ -6723,7 +6685,7 @@ public class NotificationManagerService extends SystemService {
public Policy getNotificationPolicy(String pkg) {
final int callingUid = Binder.getCallingUid();
UserHandle zenUser = getCallingZenUser();
- if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+ if (!canManageGlobalZenPolicy(pkg, callingUid)) {
return mZenModeHelper.getNotificationPolicyFromImplicitZenRule(zenUser, pkg);
}
final long identity = Binder.clearCallingIdentity();
@@ -6760,8 +6722,7 @@ public class NotificationManagerService extends SystemService {
UserHandle zenUser = getCallingZenUser();
boolean isSystemCaller = isCallerSystemOrSystemUiOrShell();
- boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
- && !canManageGlobalZenPolicy(pkg, callingUid);
+ boolean shouldApplyAsImplicitRule = !canManageGlobalZenPolicy(pkg, callingUid);
final long identity = Binder.clearCallingIdentity();
try {
@@ -6953,7 +6914,8 @@ public class NotificationManagerService extends SystemService {
android.Manifest.permission.INTERACT_ACROSS_USERS,
"setNotificationListenerAccessGrantedForUser for user " + userId);
}
- if (mUmInternal.isVisibleBackgroundFullUser(userId)) {
+ if (!managedServicesConcurrentMultiuser()
+ && mUmInternal.isVisibleBackgroundFullUser(userId)) {
// The main use case for visible background users is the Automotive multi-display
// configuration where a passenger can use a secondary display while the driver is
// using the main display. NotificationListeners is designed only for the current
@@ -8219,9 +8181,6 @@ public class NotificationManagerService extends SystemService {
@Override
public void setDeviceEffectsApplier(DeviceEffectsApplier applier) {
- if (!android.app.Flags.modesApi()) {
- return;
- }
if (mZenModeHelper == null) {
throw new IllegalStateException("ZenModeHelper is not yet ready!");
}
@@ -13165,7 +13124,8 @@ public class NotificationManagerService extends SystemService {
@Override
public void onUserUnlocked(int user) {
- if (mUmInternal.isVisibleBackgroundFullUser(user)) {
+ if (!managedServicesConcurrentMultiuser()
+ && mUmInternal.isVisibleBackgroundFullUser(user)) {
// The main use case for visible background users is the Automotive
// multi-display configuration where a passenger can use a secondary
// display while the driver is using the main display.
@@ -13805,7 +13765,7 @@ public class NotificationManagerService extends SystemService {
// TODO (b/73052211): if the ranking update changed the notification type,
// cancel notifications for NLSes that can't see them anymore
for (final ManagedServiceInfo serviceInfo : getServices()) {
- if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
+ if (!serviceInfo.isEnabledForUser() || !isInteractionVisibleToListener(
serviceInfo, ActivityManager.getCurrentUser())) {
continue;
}
@@ -13833,7 +13793,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
public void notifyListenerHintsChangedLocked(final int hints) {
for (final ManagedServiceInfo serviceInfo : getServices()) {
- if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
+ if (!serviceInfo.isEnabledForUser() || !isInteractionVisibleToListener(
serviceInfo, ActivityManager.getCurrentUser())) {
continue;
}
@@ -13889,7 +13849,7 @@ public class NotificationManagerService extends SystemService {
public void notifyInterruptionFilterChanged(final int interruptionFilter) {
for (final ManagedServiceInfo serviceInfo : getServices()) {
- if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
+ if (!serviceInfo.isEnabledForUser() || !isInteractionVisibleToListener(
serviceInfo, ActivityManager.getCurrentUser())) {
continue;
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 6c0035b82a86..f680ce722e81 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -514,11 +514,14 @@ interface NotificationRecordLogger {
final int fsi_state;
final boolean is_locked;
final int age_in_minutes;
+ final boolean is_promoted_ongoing;
+ final boolean has_promotable_characteristics;
@DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
NotificationReported(NotificationRecordPair p,
NotificationReportedEvent eventType, int position, int buzzBeepBlink,
InstanceId groupId) {
+ final Notification notification = p.r.getSbn().getNotification();
this.event_id = eventType.getId();
this.uid = p.r.getUid();
this.package_name = p.r.getSbn().getPackageName();
@@ -527,8 +530,8 @@ interface NotificationRecordLogger {
this.channel_id_hash = p.getChannelIdHash();
this.group_id_hash = p.getGroupIdHash();
this.group_instance_id = (groupId == null) ? 0 : groupId.getId();
- this.is_group_summary = p.r.getSbn().getNotification().isGroupSummary();
- this.category = p.r.getSbn().getNotification().category;
+ this.is_group_summary = notification.isGroupSummary();
+ this.category = notification.category;
this.style = p.getStyle();
this.num_people = p.getNumPeople();
this.position = position;
@@ -542,22 +545,18 @@ interface NotificationRecordLogger {
this.assistant_ranking_score = p.r.getRankingScore();
this.is_ongoing = p.r.getSbn().isOngoing();
this.is_foreground_service = NotificationRecordLogger.isForegroundService(p.r);
- this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter();
+ this.timeout_millis = notification.getTimeoutAfter();
this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r);
-
- final boolean hasFullScreenIntent =
- p.r.getSbn().getNotification().fullScreenIntent != null;
-
- final boolean hasFsiRequestedButDeniedFlag = (p.r.getSbn().getNotification().flags
- & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
-
+ final boolean hasFullScreenIntent = notification.fullScreenIntent != null;
+ final boolean hasFsiRequestedButDeniedFlag =
+ (notification.flags & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
this.fsi_state = NotificationRecordLogger.getFsiState(
hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
-
this.is_locked = p.r.isLocked();
-
this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
- p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().getWhen());
+ p.r.getSbn().getPostTime(), notification.getWhen());
+ this.is_promoted_ongoing = notification.isPromotedOngoing();
+ this.has_promotable_characteristics = notification.hasPromotableCharacteristics();
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index fc0a7764963e..e0e3fbaf49f1 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -78,7 +78,9 @@ class NotificationRecordLoggerImpl implements NotificationRecordLogger {
notificationReported.post_duration_millis,
notificationReported.fsi_state,
notificationReported.is_locked,
- notificationReported.age_in_minutes);
+ notificationReported.age_in_minutes,
+ notificationReported.is_promoted_ongoing,
+ notificationReported.has_promotable_characteristics);
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index c305d66c24c1..bc987ed21251 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -183,13 +183,8 @@ public class NotificationShellCmd extends ShellCommand {
interruptionFilter = INTERRUPTION_FILTER_ALL;
}
final int filter = interruptionFilter;
- if (android.app.Flags.modesApi()) {
- mBinderService.setInterruptionFilter(callingPackage, filter,
- /* fromUser= */ true);
- } else {
- mBinderService.setInterruptionFilter(callingPackage, filter,
- /* fromUser= */ false);
- }
+ mBinderService.setInterruptionFilter(callingPackage, filter,
+ /* fromUser= */ true);
}
break;
case "allow_dnd": {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3974c839fd38..fff812c038e7 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -42,6 +42,7 @@ import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_P
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE;
import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_PROMOTABLE;
import android.annotation.FlaggedApi;
@@ -233,11 +234,9 @@ public class PreferencesHelper implements RankingConfig {
private SparseBooleanArray mLockScreenShowNotifications;
private SparseBooleanArray mLockScreenPrivateNotifications;
private boolean mIsMediaNotificationFilteringEnabled;
- // When modes_api flag is enabled, this value only tracks whether the current user has any
- // channels marked as "priority channels", but not necessarily whether they are permitted
- // to bypass DND by current zen policy.
- // TODO: b/310620812 - Rename to be more accurate when modes_api flag is inlined.
- private boolean mCurrentUserHasChannelsBypassingDnd;
+ // Whether the current user has any channels marked as "priority channels" -- but not
+ // necessarily whether they are permitted to bypass DND by current zen policy.
+ private boolean mCurrentUserHasPriorityChannels;
private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
private final boolean mShowReviewPermissionsNotification;
@@ -288,7 +287,7 @@ public class PreferencesHelper implements RankingConfig {
if (!TAG_RANKING.equals(tag)) return;
final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
- boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
+ boolean upgradeForBubbles = xmlVersion >= XML_VERSION_BUBBLES_UPGRADE;
boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION);
if (mShowReviewPermissionsNotification
&& (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION)) {
@@ -339,15 +338,19 @@ public class PreferencesHelper implements RankingConfig {
}
boolean skipWarningLogged = false;
boolean skipGroupWarningLogged = false;
- boolean hasSAWPermission = false;
- if (upgradeForBubbles && uid != UNKNOWN_UID) {
- hasSAWPermission = mAppOps.noteOpNoThrow(
- OP_SYSTEM_ALERT_WINDOW, uid, name, null,
- "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
- }
- int bubblePref = hasSAWPermission
- ? BUBBLE_PREFERENCE_ALL
- : parser.getAttributeInt(null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
+ int bubblePref = parser.getAttributeInt(null, ATT_ALLOW_BUBBLE,
+ DEFAULT_BUBBLE_PREFERENCE);
+ boolean bubbleLocked = (parser.getAttributeInt(null,
+ ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS) & USER_LOCKED_BUBBLE)
+ != 0;
+ if (!bubbleLocked
+ && upgradeForBubbles
+ && uid != UNKNOWN_UID
+ && mAppOps.noteOpNoThrow(OP_SYSTEM_ALERT_WINDOW, uid, name, null,
+ "check-notif-bubble") == AppOpsManager.MODE_ALLOWED) {
+ // User hasn't changed bubble pref & the app has SAW, so allow all bubbles.
+ bubblePref = BUBBLE_PREFERENCE_ALL;
+ }
int appImportance = parser.getAttributeInt(null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
// when data is loaded from disk it's loaded as USER_ALL, but restored data that
@@ -1063,7 +1066,7 @@ public class PreferencesHelper implements RankingConfig {
r.groups.put(group.getId(), group);
}
if (needsDndChange) {
- updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+ updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi);
}
if (android.app.Flags.nmBinderPerfCacheChannels() && changed) {
invalidateNotificationChannelGroupCache();
@@ -1150,7 +1153,7 @@ public class PreferencesHelper implements RankingConfig {
existing.setBypassDnd(bypassDnd);
needsPolicyFileChange = true;
- if (bypassDnd != mCurrentUserHasChannelsBypassingDnd
+ if (bypassDnd != mCurrentUserHasPriorityChannels
|| previousExistingImportance != existing.getImportance()) {
needsDndChange = true;
}
@@ -1214,7 +1217,7 @@ public class PreferencesHelper implements RankingConfig {
}
r.channels.put(channel.getId(), channel);
- if (channel.canBypassDnd() != mCurrentUserHasChannelsBypassingDnd) {
+ if (channel.canBypassDnd() != mCurrentUserHasPriorityChannels) {
needsDndChange = true;
}
MetricsLogger.action(getChannelLog(channel, pkg).setType(
@@ -1224,7 +1227,7 @@ public class PreferencesHelper implements RankingConfig {
}
if (needsDndChange) {
- updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+ updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi);
}
if (android.app.Flags.nmBinderPerfCacheChannels() && needsPolicyFileChange) {
@@ -1317,14 +1320,14 @@ public class PreferencesHelper implements RankingConfig {
// relevantly affected without the parent channel already having been.
}
- if (updatedChannel.canBypassDnd() != mCurrentUserHasChannelsBypassingDnd
+ if (updatedChannel.canBypassDnd() != mCurrentUserHasPriorityChannels
|| channel.getImportance() != updatedChannel.getImportance()) {
needsDndChange = true;
changed = true;
}
}
if (needsDndChange) {
- updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+ updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi);
}
if (changed) {
if (android.app.Flags.nmBinderPerfCacheChannels()) {
@@ -1550,7 +1553,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (channelBypassedDnd) {
- updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+ updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi);
}
if (android.app.Flags.nmBinderPerfCacheChannels() && deletedChannel) {
@@ -1745,7 +1748,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (groupBypassedDnd) {
- updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+ updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi);
}
if (android.app.Flags.nmBinderPerfCacheChannels()) {
if (deletedChannels.size() > 0) {
@@ -1906,8 +1909,8 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (!deletedChannelIds.isEmpty()) {
- if (mCurrentUserHasChannelsBypassingDnd) {
- updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+ if (mCurrentUserHasPriorityChannels) {
+ updateCurrentUserHasPriorityChannels(callingUid, fromSystemOrSystemUi);
}
if (android.app.Flags.nmBinderPerfCacheChannels()) {
invalidateNotificationChannelCache();
@@ -2098,7 +2101,7 @@ public class PreferencesHelper implements RankingConfig {
}
/**
- * Syncs {@link #mCurrentUserHasChannelsBypassingDnd} with the current user's notification
+ * Syncs {@link #mCurrentUserHasPriorityChannels} with the current user's notification
* policy before updating. Must be called:
* <ul>
* <li>On system init, after channels and DND configurations are loaded.
@@ -2106,22 +2109,23 @@ public class PreferencesHelper implements RankingConfig {
* <li>If users are removed (the removed user could've been a profile of the current one).
* </ul>
*/
- void syncChannelsBypassingDnd() {
- mCurrentUserHasChannelsBypassingDnd =
+ void syncHasPriorityChannels() {
+ mCurrentUserHasPriorityChannels =
(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).state
- & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
+ & NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS) != 0;
- updateCurrentUserHasChannelsBypassingDnd(/* callingUid= */ Process.SYSTEM_UID,
+ updateCurrentUserHasPriorityChannels(/* callingUid= */ Process.SYSTEM_UID,
/* fromSystemOrSystemUi= */ true);
}
/**
* Updates the user's NotificationPolicy based on whether the current userId has channels
- * bypassing DND. It should be called whenever a channel is created, updated, or deleted, or
- * when the current user (or its profiles) change.
+ * marked as "priority" (which might bypass DND, depending on the zen rule details). It should
+ * be called whenever a channel is created, updated, or deleted, or when the current user (or
+ * its profiles) change.
*/
// TODO: b/368247671 - remove fromSystemOrSystemUi argument when modes_ui is inlined.
- private void updateCurrentUserHasChannelsBypassingDnd(int callingUid,
+ private void updateCurrentUserHasPriorityChannels(int callingUid,
boolean fromSystemOrSystemUi) {
ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
@@ -2149,13 +2153,13 @@ public class PreferencesHelper implements RankingConfig {
}
}
boolean haveBypassingApps = candidatePkgs.size() > 0;
- if (mCurrentUserHasChannelsBypassingDnd != haveBypassingApps) {
- mCurrentUserHasChannelsBypassingDnd = haveBypassingApps;
+ if (mCurrentUserHasPriorityChannels != haveBypassingApps) {
+ mCurrentUserHasPriorityChannels = haveBypassingApps;
if (android.app.Flags.modesUi()) {
mZenModeHelper.updateHasPriorityChannels(UserHandle.CURRENT,
- mCurrentUserHasChannelsBypassingDnd);
+ mCurrentUserHasPriorityChannels);
} else {
- updateZenPolicy(mCurrentUserHasChannelsBypassingDnd, callingUid,
+ updateZenPolicy(mCurrentUserHasPriorityChannels, callingUid,
fromSystemOrSystemUi);
}
}
@@ -2188,16 +2192,20 @@ public class PreferencesHelper implements RankingConfig {
policy.priorityCategories, policy.priorityCallSenders,
policy.priorityMessageSenders, policy.suppressedVisualEffects,
(areChannelsBypassingDnd
- ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND : 0),
+ ? NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS : 0),
policy.priorityConversationSenders),
fromSystemOrSystemUi ? ZenModeConfig.ORIGIN_SYSTEM
: ZenModeConfig.ORIGIN_APP,
callingUid);
}
- // TODO: b/310620812 - rename to hasPriorityChannels() when modes_api is inlined.
- public boolean areChannelsBypassingDnd() {
- return mCurrentUserHasChannelsBypassingDnd;
+ /**
+ * Whether the current user has any channels marked as "priority channels"
+ * ({@link NotificationChannel#canBypassDnd}), but not necessarily whether they are permitted
+ * to bypass the filters set by the current zen policy.
+ */
+ public boolean hasPriorityChannels() {
+ return mCurrentUserHasPriorityChannels;
}
/**
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index fcc5e9771f94..ec9a2db52de9 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -16,7 +16,7 @@
package com.android.server.notification;
-import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
+import static android.app.NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_NONE;
import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY;
@@ -285,11 +285,10 @@ class ZenModeEventLogger {
return true;
}
- if (Flags.modesApi() && hasActiveRuleCountDiff()) {
- // Rules with INTERRUPTION_FILTER_ALL were always possible but before MODES_API
- // they were completely useless; now they can apply effects, so we want to log
- // when they become active/inactive, even though DND itself (as in "notification
- // blocking") is off.
+ if (hasActiveRuleCountDiff()) {
+ // Rules with INTERRUPTION_FILTER_ALL can apply effects, so we want to log when they
+ // become active/inactive, even though DND itself (as in "notification blocking")
+ // is off.
return true;
}
@@ -331,7 +330,7 @@ class ZenModeEventLogger {
}
}
- if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
+ if (mNewZenMode == ZEN_MODE_OFF) {
// If the mode is OFF -> OFF then there cannot be any *effective* change to policy.
// (Note that, in theory, a policy diff is impossible since we don't merge the
// policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check).
@@ -439,24 +438,14 @@ class ZenModeEventLogger {
// Determine the number of (automatic & manual) rules active after the change takes place.
int getNumRulesActive() {
- if (!Flags.modesApi()) {
- // If the zen mode has turned off, that means nothing can be active.
- if (mNewZenMode == ZEN_MODE_OFF) {
- return 0;
- }
- }
return numActiveRulesInConfig(mNewConfig);
}
/**
- * Return a list of the types of each of the active rules in the configuration.
- * Only available when {@code MODES_API} is active; otherwise returns an empty list.
+ * Return a list of the types of each of the active rules in the configuration (sorted by
+ * the numerical value of the type, and including duplicates).
*/
int[] getActiveRuleTypes() {
- if (!Flags.modesApi()) {
- return new int[0];
- }
-
ArrayList<Integer> activeTypes = new ArrayList<>();
List<ZenRule> activeRules = activeRulesList(mNewConfig);
if (activeRules.size() == 0) {
@@ -476,77 +465,10 @@ class ZenModeEventLogger {
return out;
}
- /**
- * Return our best guess as to whether the changes observed are due to a user action.
- * Note that this (before {@code MODES_API}) won't be 100% accurate as we can't necessarily
- * distinguish between a system uid call indicating "user interacted with Settings" vs "a
- * system app changed something automatically".
- */
+ /** Return whether the changes observed are due to a user action. */
boolean getIsUserAction() {
- if (Flags.modesApi()) {
- return mOrigin == ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI
- || mOrigin == ZenModeConfig.ORIGIN_USER_IN_APP;
- }
-
- // Approach for pre-MODES_API:
- // - if manual rule turned on or off, the calling UID is system, and the new manual
- // rule does not have an enabler set, guess that this is likely to be a user action.
- // This may represent a system app turning on DND automatically, but we guess "user"
- // in this case.
- // - note that this has a known failure mode of "manual rule turning off
- // automatically after the default time runs out". We currently have no way
- // of distinguishing this case from a user manually turning off the rule.
- // - the reason for checking the enabler field is that a call may look like it's
- // coming from a system UID, but if an enabler is set then the request came
- // from an external source. "enabler" will be blank when manual rule is turned
- // on from Quick Settings or Settings.
- // - if an automatic rule's state changes in whether it is "enabled", then
- // that is probably a user action.
- // - if an automatic rule goes from "not snoozing" to "snoozing", that is probably
- // a user action; that means that the user temporarily turned off DND associated
- // with that rule.
- // - if an automatic rule becomes active but does *not* change in its enabled state
- // (covered by a previous case anyway), we guess that this is an automatic change.
- // - if a rule is added or removed and the call comes from the system, we guess that
- // this is a user action (as system rules can't be added or removed without a user
- // action).
- switch (getChangedRuleType()) {
- case RULE_TYPE_MANUAL:
- // TODO(b/278888961): Distinguish the automatically-turned-off state
- return isFromSystemOrSystemUi() && (getNewManualRuleEnabler() == null);
- case RULE_TYPE_AUTOMATIC:
- for (ZenModeDiff.RuleDiff d : getChangedAutomaticRules().values()) {
- if (d.wasAdded() || d.wasRemoved()) {
- // If the change comes from system, a rule being added/removed indicates
- // a likely user action. From an app, it's harder to know for sure.
- return isFromSystemOrSystemUi();
- }
- ZenModeDiff.FieldDiff enabled = d.getDiffForField(
- ZenModeDiff.RuleDiff.FIELD_ENABLED);
- if (enabled != null && enabled.hasDiff()) {
- return true;
- }
- ZenModeDiff.FieldDiff snoozing = d.getDiffForField(
- ZenModeDiff.RuleDiff.FIELD_SNOOZING);
- if (snoozing != null && snoozing.hasDiff() && (boolean) snoozing.to()) {
- return true;
- }
- }
- // If the change was in an automatic rule and none of the "probably triggered
- // by a user" cases apply, then it's probably an automatic change.
- return false;
- case RULE_TYPE_UNKNOWN:
- default:
- }
-
- // If the change wasn't in a rule, but was in the zen policy: consider to be user action
- // if the calling uid is system
- if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
- return mCallingUid == Process.SYSTEM_UID;
- }
-
- // don't know, or none of the other things triggered; assume not a user action
- return false;
+ return mOrigin == ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI
+ || mOrigin == ZenModeConfig.ORIGIN_USER_IN_APP;
}
boolean isFromSystemOrSystemUi() {
@@ -587,7 +509,7 @@ class ZenModeEventLogger {
*/
@Nullable
byte[] getDNDPolicyProto() {
- if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
+ if (mNewZenMode == ZEN_MODE_OFF) {
return null;
}
@@ -628,13 +550,10 @@ class ZenModeEventLogger {
mNewPolicy.allowMessagesFrom()));
proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
mNewPolicy.allowConversationsFrom());
-
- if (Flags.modesApi()) {
- proto.write(DNDPolicyProto.ALLOW_CHANNELS,
- mNewPolicy.allowPriorityChannels()
- ? CHANNEL_POLICY_PRIORITY
- : CHANNEL_POLICY_NONE);
- }
+ proto.write(DNDPolicyProto.ALLOW_CHANNELS,
+ mNewPolicy.allowPriorityChannels()
+ ? CHANNEL_POLICY_PRIORITY
+ : CHANNEL_POLICY_NONE);
} else {
Log.wtf(TAG, "attempted to write zen mode log event with null policy");
}
@@ -648,14 +567,14 @@ class ZenModeEventLogger {
*/
boolean getAreChannelsBypassing() {
if (mNewPolicy != null) {
- return (mNewPolicy.state & STATE_CHANNELS_BYPASSING_DND) != 0;
+ return (mNewPolicy.state & STATE_HAS_PRIORITY_CHANNELS) != 0;
}
return false;
}
private boolean hasChannelsBypassingDiff() {
boolean prevChannelsBypassing = mPrevPolicy != null
- ? (mPrevPolicy.state & STATE_CHANNELS_BYPASSING_DND) != 0 : false;
+ ? (mPrevPolicy.state & STATE_HAS_PRIORITY_CHANNELS) != 0 : false;
return prevChannelsBypassing != getAreChannelsBypassing();
}
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index bdca555707e3..87ae78195ff5 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -19,7 +19,6 @@ package com.android.server.notification;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
-import android.app.Flags;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -146,16 +145,12 @@ public class ZenModeFiltering {
// Returns whether the record is permitted to bypass DND when the zen mode is
// ZEN_MODE_IMPORTANT_INTERRUPTIONS. This depends on whether the record's package priority is
- // marked as PRIORITY_MAX (an indication of it belonging to a priority channel), and, if
- // the modes_api flag is on, whether the given policy permits priority channels to bypass.
- // TODO: b/310620812 - simplify when modes_api is inlined.
+ // marked as PRIORITY_MAX (an indication of it belonging to a priority channel), and whether the
+ // given policy permits priority channels to bypass.
private boolean canRecordBypassDnd(NotificationRecord record,
NotificationManager.Policy policy) {
boolean inPriorityChannel = record.getPackagePriority() == Notification.PRIORITY_MAX;
- if (Flags.modesApi()) {
- return inPriorityChannel && policy.allowPriorityChannels();
- }
- return inPriorityChannel;
+ return inPriorityChannel && policy.allowPriorityChannels();
}
/**
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b39b6fde6258..889df512dd60 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -157,6 +157,12 @@ public class ZenModeHelper {
static final int RULE_LIMIT_PER_PACKAGE = 100;
private static final Duration DELETED_RULE_KEPT_FOR = Duration.ofDays(30);
+ /**
+ * Amount of time since last activation after which implicit rules that have never been
+ * customized by the user are automatically cleaned up.
+ */
+ private static final Duration IMPLICIT_RULE_KEPT_FOR = Duration.ofDays(30);
+
private static final int MAX_ICON_RESOURCE_NAME_LENGTH = 1000;
/**
@@ -326,9 +332,6 @@ public class ZenModeHelper {
* applied immediately.
*/
void setDeviceEffectsApplier(@NonNull DeviceEffectsApplier deviceEffectsApplier) {
- if (!Flags.modesApi()) {
- return;
- }
synchronized (mConfigLock) {
if (mDeviceEffectsApplier != null) {
throw new IllegalStateException("Already set up a DeviceEffectsApplier!");
@@ -350,11 +353,6 @@ public class ZenModeHelper {
}
}
- // TODO: b/310620812 - Remove when MODES_API is inlined (no more callers).
- public void onUserUnlocked(int user) {
- loadConfigForUser(user, "onUserUnlocked");
- }
-
void setPriorityOnlyDndExemptPackages(String[] packages) {
mPriorityOnlyDndExemptPackages = packages;
}
@@ -385,21 +383,6 @@ public class ZenModeHelper {
return NotificationManager.zenModeToInterruptionFilter(mZenMode);
}
- // TODO: b/310620812 - Remove when MODES_API is inlined (no more callers).
- public void requestFromListener(ComponentName name, int filter, int callingUid,
- boolean fromSystemOrSystemUi) {
- final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
- if (newZen != -1) {
- // This change is known to be for UserHandle.CURRENT because NLSes for
- // background users are unbound.
- setManualZenMode(UserHandle.CURRENT, newZen, null,
- fromSystemOrSystemUi ? ORIGIN_SYSTEM : ORIGIN_APP,
- /* reason= */ "listener:" + (name != null ? name.flattenToShortString() : null),
- /* caller= */ name != null ? name.getPackageName() : null,
- callingUid);
- }
- }
-
public void setSuppressedEffects(long suppressedEffects) {
if (mSuppressedEffects == suppressedEffects) return;
mSuppressedEffects = suppressedEffects;
@@ -414,33 +397,24 @@ public class ZenModeHelper {
return mZenMode;
}
- // TODO: b/310620812 - Make private (or inline) when MODES_API is inlined.
- public List<ZenRule> getZenRules(UserHandle user, int callingUid) {
- List<ZenRule> rules = new ArrayList<>();
+ /**
+ * Get the list of {@link AutomaticZenRule} instances that the calling package can manage
+ * (which means the owned rules for a regular app, and every rule for system callers) together
+ * with their ids.
+ */
+ Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user, int callingUid) {
+ HashMap<String, AutomaticZenRule> rules = new HashMap<>();
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) return rules;
+
for (ZenRule rule : config.automaticRules.values()) {
if (canManageAutomaticZenRule(rule, callingUid)) {
- rules.add(rule);
+ rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
}
}
+ return rules;
}
- return rules;
- }
-
- /**
- * Get the list of {@link AutomaticZenRule} instances that the calling package can manage
- * (which means the owned rules for a regular app, and every rule for system callers) together
- * with their ids.
- */
- Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user, int callingUid) {
- List<ZenRule> ruleList = getZenRules(user, callingUid);
- HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size());
- for (ZenRule rule : ruleList) {
- rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
- }
- return rules;
}
public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id, int callingUid) {
@@ -511,9 +485,6 @@ public class ZenModeHelper {
@GuardedBy("mConfigLock")
private ZenRule maybeRestoreRemovedRule(ZenModeConfig config, String pkg, ZenRule ruleToAdd,
AutomaticZenRule azrToAdd, @ConfigOrigin int origin) {
- if (!Flags.modesApi()) {
- return ruleToAdd;
- }
String deletedKey = ZenModeConfig.deletedRuleKey(ruleToAdd);
if (deletedKey == null) {
// Couldn't calculate the deletedRuleKey (condition or pkg null?). This should
@@ -561,9 +532,6 @@ public class ZenModeHelper {
*/
private static void maybeReplaceDefaultRule(ZenModeConfig config, @Nullable ZenRule oldRule,
AutomaticZenRule rule) {
- if (!Flags.modesApi()) {
- return;
- }
if (rule.getType() == AutomaticZenRule.TYPE_BEDTIME
&& (oldRule == null || oldRule.type != rule.getType())) {
// Note: we must not verify canManageAutomaticZenRule here, since most likely they
@@ -572,7 +540,7 @@ public class ZenModeHelper {
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
if (sleepingRule != null
&& !sleepingRule.enabled
- && sleepingRule.canBeUpdatedByApp() /* meaning it's not user-customized */) {
+ && !sleepingRule.isUserModified()) {
config.automaticRules.remove(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
}
}
@@ -599,18 +567,10 @@ public class ZenModeHelper {
}
ZenModeConfig newConfig = config.copy();
ZenModeConfig.ZenRule newRule = requireNonNull(newConfig.automaticRules.get(ruleId));
- if (!Flags.modesApi()) {
- if (newRule.enabled != automaticZenRule.isEnabled()) {
- dispatchOnAutomaticRuleStatusChanged(config.user, newRule.getPkg(), ruleId,
- automaticZenRule.isEnabled()
- ? AUTOMATIC_RULE_STATUS_ENABLED
- : AUTOMATIC_RULE_STATUS_DISABLED);
- }
- }
boolean updated = populateZenRule(newRule.pkg, automaticZenRule, newConfig, newRule,
origin, /* isNew= */ false);
- if (Flags.modesApi() && !updated) {
+ if (!updated) {
// Bail out so we don't have the side effects of updating a rule (i.e. dropping
// condition) when no changes happen.
return true;
@@ -643,10 +603,6 @@ public class ZenModeHelper {
*/
void applyGlobalZenModeAsImplicitZenRule(UserHandle user, String callingPkg, int callingUid,
int zenMode) {
- if (!android.app.Flags.modesApi()) {
- Log.wtf(TAG, "applyGlobalZenModeAsImplicitZenRule called with flag off!");
- return;
- }
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) {
@@ -712,10 +668,6 @@ public class ZenModeHelper {
*/
void applyGlobalPolicyAsImplicitZenRule(UserHandle user, String callingPkg, int callingUid,
NotificationManager.Policy policy) {
- if (!android.app.Flags.modesApi()) {
- Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
- return;
- }
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) {
@@ -772,10 +724,6 @@ public class ZenModeHelper {
*/
@Nullable
Policy getNotificationPolicyFromImplicitZenRule(UserHandle user, String callingPkg) {
- if (!android.app.Flags.modesApi()) {
- Log.wtf(TAG, "getNotificationPolicyFromImplicitZenRule called with flag off!");
- return getNotificationPolicy(user);
- }
synchronized (mConfigLock) {
ZenModeConfig config = getConfigLocked(user);
if (config == null) {
@@ -814,7 +762,6 @@ public class ZenModeHelper {
.appendPath(pkg)
.build();
rule.enabled = true;
- rule.modified = false;
rule.component = null;
rule.configurationActivity = null;
return rule;
@@ -918,15 +865,12 @@ public class ZenModeHelper {
private void maybePreserveRemovedRule(ZenModeConfig config, ZenRule ruleToRemove,
@ConfigOrigin int origin) {
- if (!Flags.modesApi()) {
- return;
- }
// If an app deletes a previously customized rule, keep it around to preserve
// the user's customization when/if it's recreated later.
// We don't try to preserve system-owned rules because their conditionIds (used as
// deletedRuleKey) are not stable. This is almost moot anyway because an app cannot
// delete a system-owned rule.
- if (origin == ORIGIN_APP && !ruleToRemove.canBeUpdatedByApp()
+ if (origin == ORIGIN_APP && ruleToRemove.isUserModified()
&& !PACKAGE_ANDROID.equals(ruleToRemove.pkg)) {
String deletedKey = ZenModeConfig.deletedRuleKey(ruleToRemove);
if (deletedKey != null) {
@@ -952,7 +896,7 @@ public class ZenModeHelper {
if (rule == null || !canManageAutomaticZenRule(rule, callingUid)) {
return Condition.STATE_UNKNOWN;
}
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
return rule.isActive() ? STATE_TRUE : STATE_FALSE;
} else {
// Buggy, does not consider snoozing!
@@ -971,16 +915,9 @@ public class ZenModeHelper {
newConfig = config.copy();
ZenRule rule = newConfig.automaticRules.get(id);
- if (Flags.modesApi()) {
- if (rule != null && canManageAutomaticZenRule(rule, callingUid)) {
- setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
- condition, origin, "setAzrState: " + rule.id, callingUid);
- }
- } else {
- ArrayList<ZenRule> rules = new ArrayList<>();
- rules.add(rule); // rule may be null and throw NPE in the next method.
- setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin,
- "setAzrState: " + (rule != null ? rule.id : "null!"), callingUid);
+ if (rule != null && canManageAutomaticZenRule(rule, callingUid)) {
+ setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+ condition, origin, "setAzrState: " + rule.id, callingUid);
}
}
}
@@ -995,13 +932,12 @@ public class ZenModeHelper {
newConfig = config.copy();
List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleConditionId, condition);
- if (Flags.modesApi()) {
- for (int i = matchingRules.size() - 1; i >= 0; i--) {
- if (!canManageAutomaticZenRule(matchingRules.get(i), callingUid)) {
- matchingRules.remove(i);
- }
+ for (int i = matchingRules.size() - 1; i >= 0; i--) {
+ if (!canManageAutomaticZenRule(matchingRules.get(i), callingUid)) {
+ matchingRules.remove(i);
}
}
+
setAutomaticZenRuleStateLocked(newConfig, matchingRules, condition, origin,
"setAzrStateFromCps: " + ruleConditionId, callingUid);
}
@@ -1013,7 +949,7 @@ public class ZenModeHelper {
if (rules == null || rules.isEmpty()) return;
if (!Flags.modesUi()) {
- if (Flags.modesApi() && condition.source == SOURCE_USER_ACTION) {
+ if (condition.source == SOURCE_USER_ACTION) {
origin = ORIGIN_USER_IN_APP; // Although coming from app, it's actually from user.
}
}
@@ -1026,7 +962,7 @@ public class ZenModeHelper {
private static void applyConditionAndReconsiderOverride(ZenRule rule, Condition condition,
int origin) {
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
if (isImplicitRuleId(rule.id)) {
// Implicit rules do not use overrides, and always apply conditions directly.
// This is compatible with the previous behavior (where the package set the
@@ -1173,8 +1109,7 @@ public class ZenModeHelper {
// if default rule wasn't user-modified use localized name
// instead of previous system name
if (currRule != null
- && !currRule.modified
- && (currRule.zenPolicyUserModifiedFields & AutomaticZenRule.FIELD_NAME) == 0
+ && (currRule.userModifiedFields & AutomaticZenRule.FIELD_NAME) == 0
&& !defaultRule.name.equals(currRule.name)) {
if (DEBUG) {
Slog.d(TAG, "Locale change - updating default zen rule name "
@@ -1184,7 +1119,7 @@ public class ZenModeHelper {
updated = true;
}
}
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
for (ZenRule rule : newConfig.automaticRules.values()) {
if (SystemZenRules.isSystemOwnedRule(rule)) {
updated |= SystemZenRules.updateTriggerDescription(mContext, rule);
@@ -1256,172 +1191,145 @@ public class ZenModeHelper {
@GuardedBy("mConfigLock")
private boolean populateZenRule(String pkg, AutomaticZenRule azr, ZenModeConfig config,
ZenRule rule, @ConfigOrigin int origin, boolean isNew) {
- if (Flags.modesApi()) {
- boolean modified = false;
- // These values can always be edited by the app, so we apply changes immediately.
- if (isNew) {
- rule.id = ZenModeConfig.newRuleId();
- rule.creationTime = mClock.millis();
- rule.component = azr.getOwner();
- rule.pkg = pkg;
- modified = true;
- }
- // Allow updating the CPS backing system rules (e.g. for custom manual -> schedule)
- if (Flags.modesUi()
- && (origin == ORIGIN_SYSTEM || origin == ORIGIN_USER_IN_SYSTEMUI)
- && Objects.equals(rule.pkg, SystemZenRules.PACKAGE_ANDROID)
- && !Objects.equals(rule.component, azr.getOwner())) {
- rule.component = azr.getOwner();
- modified = true;
- }
+ boolean modified = false;
+ // These values can always be edited by the app, so we apply changes immediately.
+ if (isNew) {
+ rule.id = ZenModeConfig.newRuleId();
+ rule.creationTime = mClock.millis();
+ rule.component = azr.getOwner();
+ rule.pkg = pkg;
+ modified = true;
+ }
- if (Flags.modesUi()) {
- if (!azr.isEnabled() && (isNew || rule.enabled)) {
- // Creating a rule as disabled, or disabling a previously enabled rule.
- // Record whodunit.
- rule.disabledOrigin = origin;
- } else if (azr.isEnabled()) {
- // Enabling or previously enabled. Clear disabler.
- rule.disabledOrigin = ORIGIN_UNKNOWN;
- }
- }
+ // Allow updating the CPS backing system rules (e.g. for custom manual -> schedule)
+ if (Flags.modesUi()
+ && (origin == ORIGIN_SYSTEM || origin == ORIGIN_USER_IN_SYSTEMUI)
+ && Objects.equals(rule.pkg, SystemZenRules.PACKAGE_ANDROID)
+ && !Objects.equals(rule.component, azr.getOwner())) {
+ rule.component = azr.getOwner();
+ modified = true;
+ }
- if (!Objects.equals(rule.conditionId, azr.getConditionId())) {
- rule.conditionId = azr.getConditionId();
- modified = true;
- }
- // This can be removed when {@link Flags#modesUi} is fully ramped up
- final boolean isWatch =
- mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
- boolean shouldPreserveCondition =
- Flags.modesApi()
- && (Flags.modesUi() || isWatch)
- && !isNew
- && origin == ORIGIN_USER_IN_SYSTEMUI
- && rule.enabled == azr.isEnabled()
- && rule.conditionId != null
- && rule.condition != null
- && rule.conditionId.equals(rule.condition.id);
- if (!shouldPreserveCondition) {
- // Do not update 'modified'. If only this changes we treat it as a no-op updateAZR.
- rule.condition = null;
- }
-
- if (rule.enabled != azr.isEnabled()) {
- rule.enabled = azr.isEnabled();
- rule.resetConditionOverride();
- modified = true;
- }
- if (!Objects.equals(rule.configurationActivity, azr.getConfigurationActivity())) {
- rule.configurationActivity = azr.getConfigurationActivity();
- modified = true;
- }
- if (rule.allowManualInvocation != azr.isManualInvocationAllowed()) {
- rule.allowManualInvocation = azr.isManualInvocationAllowed();
- modified = true;
- }
- if (!Flags.modesUi()) {
- String iconResName = drawableResIdToResName(rule.pkg, azr.getIconResId());
- if (!Objects.equals(rule.iconResName, iconResName)) {
- rule.iconResName = iconResName;
- modified = true;
- }
- }
- if (!Objects.equals(rule.triggerDescription, azr.getTriggerDescription())) {
- rule.triggerDescription = azr.getTriggerDescription();
- modified = true;
+ if (Flags.modesUi()) {
+ if (!azr.isEnabled() && (isNew || rule.enabled)) {
+ // Creating a rule as disabled, or disabling a previously enabled rule.
+ // Record whodunit.
+ rule.disabledOrigin = origin;
+ } else if (azr.isEnabled()) {
+ // Enabling or previously enabled. Clear disabler.
+ rule.disabledOrigin = ORIGIN_UNKNOWN;
}
- if (rule.type != azr.getType()) {
- rule.type = azr.getType();
+ }
+
+ if (!Objects.equals(rule.conditionId, azr.getConditionId())) {
+ rule.conditionId = azr.getConditionId();
+ modified = true;
+ }
+ // This can be removed when {@link Flags#modesUi} is fully ramped up
+ final boolean isWatch =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ boolean shouldPreserveCondition =
+ (Flags.modesUi() || isWatch)
+ && !isNew
+ && origin == ORIGIN_USER_IN_SYSTEMUI
+ && rule.enabled == azr.isEnabled()
+ && rule.conditionId != null
+ && rule.condition != null
+ && rule.conditionId.equals(rule.condition.id);
+ if (!shouldPreserveCondition) {
+ // Do not update 'modified'. If only this changes we treat it as a no-op updateAZR.
+ rule.condition = null;
+ }
+
+ if (rule.enabled != azr.isEnabled()) {
+ rule.enabled = azr.isEnabled();
+ rule.resetConditionOverride();
+ modified = true;
+ }
+ if (!Objects.equals(rule.configurationActivity, azr.getConfigurationActivity())) {
+ rule.configurationActivity = azr.getConfigurationActivity();
+ modified = true;
+ }
+ if (rule.allowManualInvocation != azr.isManualInvocationAllowed()) {
+ rule.allowManualInvocation = azr.isManualInvocationAllowed();
+ modified = true;
+ }
+ if (!Flags.modesUi()) {
+ String iconResName = drawableResIdToResName(rule.pkg, azr.getIconResId());
+ if (!Objects.equals(rule.iconResName, iconResName)) {
+ rule.iconResName = iconResName;
modified = true;
}
- // TODO: b/310620812 - Remove this once FLAG_MODES_API is inlined.
- rule.modified = azr.isModified();
+ }
+ if (!Objects.equals(rule.triggerDescription, azr.getTriggerDescription())) {
+ rule.triggerDescription = azr.getTriggerDescription();
+ modified = true;
+ }
+ if (rule.type != azr.getType()) {
+ rule.type = azr.getType();
+ modified = true;
+ }
- // Name is treated differently than other values:
- // App is allowed to update name if the name was not modified by the user (even if
- // other values have been modified). In this way, if the locale of an app changes,
- // i18n of the rule name can still occur even if the user has customized the rule
- // contents.
- String previousName = rule.name;
- if (isNew || doesOriginAlwaysUpdateValues(origin)
- || (rule.userModifiedFields & AutomaticZenRule.FIELD_NAME) == 0) {
- rule.name = azr.getName();
- modified |= !Objects.equals(rule.name, previousName);
- }
+ // Name is treated differently than other values:
+ // App is allowed to update name if the name was not modified by the user (even if
+ // other values have been modified). In this way, if the locale of an app changes,
+ // i18n of the rule name can still occur even if the user has customized the rule
+ // contents.
+ String previousName = rule.name;
+ if (isNew || doesOriginAlwaysUpdateValues(origin)
+ || (rule.userModifiedFields & AutomaticZenRule.FIELD_NAME) == 0) {
+ rule.name = azr.getName();
+ modified |= !Objects.equals(rule.name, previousName);
+ }
- // For the remaining values, rules can always have all values updated if:
- // * the rule is newly added, or
- // * the request comes from an origin that can always update values, like the user, or
- // * the rule has not yet been user modified, and thus can be updated by the app.
- boolean updateValues = isNew || doesOriginAlwaysUpdateValues(origin)
- || rule.canBeUpdatedByApp();
+ // For the remaining values, rules can always have all values updated if:
+ // * the rule is newly added, or
+ // * the request comes from an origin that can always update values, like the user, or
+ // * the rule has not yet been user modified, and thus can be updated by the app.
+ boolean updateValues = isNew || doesOriginAlwaysUpdateValues(origin)
+ || !rule.isUserModified();
- // For all other values, if updates are not allowed, we discard the update.
- if (!updateValues) {
- return modified;
- }
+ // For all other values, if updates are not allowed, we discard the update.
+ if (!updateValues) {
+ return modified;
+ }
- // Updates the bitmasks if the origin of the change is the user.
- boolean updateBitmask = (origin == ORIGIN_USER_IN_SYSTEMUI);
+ // Updates the bitmasks if the origin of the change is the user.
+ boolean updateBitmask = (origin == ORIGIN_USER_IN_SYSTEMUI);
- if (updateBitmask && !TextUtils.equals(previousName, azr.getName())) {
- rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
+ if (updateBitmask && !TextUtils.equals(previousName, azr.getName())) {
+ rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
+ }
+ int newZenMode = NotificationManager.zenModeFromInterruptionFilter(
+ azr.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+ if (rule.zenMode != newZenMode) {
+ rule.zenMode = newZenMode;
+ if (updateBitmask) {
+ rule.userModifiedFields |= AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
}
- int newZenMode = NotificationManager.zenModeFromInterruptionFilter(
- azr.getInterruptionFilter(), Global.ZEN_MODE_OFF);
- if (rule.zenMode != newZenMode) {
- rule.zenMode = newZenMode;
+ modified = true;
+ }
+
+ if (Flags.modesUi()) {
+ String iconResName = drawableResIdToResName(rule.pkg, azr.getIconResId());
+ if (!Objects.equals(rule.iconResName, iconResName)) {
+ rule.iconResName = iconResName;
if (updateBitmask) {
- rule.userModifiedFields |= AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+ rule.userModifiedFields |= AutomaticZenRule.FIELD_ICON;
}
modified = true;
}
+ }
- if (Flags.modesUi()) {
- String iconResName = drawableResIdToResName(rule.pkg, azr.getIconResId());
- if (!Objects.equals(rule.iconResName, iconResName)) {
- rule.iconResName = iconResName;
- if (updateBitmask) {
- rule.userModifiedFields |= AutomaticZenRule.FIELD_ICON;
- }
- modified = true;
- }
- }
-
- // Updates the bitmask and values for all policy fields, based on the origin.
- modified |= updatePolicy(config, rule, azr.getZenPolicy(), updateBitmask, isNew);
+ // Updates the bitmask and values for all policy fields, based on the origin.
+ modified |= updatePolicy(config, rule, azr.getZenPolicy(), updateBitmask, isNew);
- // Updates the bitmask and values for all device effect fields, based on the origin.
- modified |= updateZenDeviceEffects(rule, azr.getDeviceEffects(),
- origin == ORIGIN_APP, updateBitmask);
+ // Updates the bitmask and values for all device effect fields, based on the origin.
+ modified |= updateZenDeviceEffects(rule, azr.getDeviceEffects(),
+ origin == ORIGIN_APP, updateBitmask);
- return modified;
- } else {
- if (rule.enabled != azr.isEnabled()) {
- rule.resetConditionOverride();
- }
- rule.name = azr.getName();
- rule.condition = null;
- rule.conditionId = azr.getConditionId();
- rule.enabled = azr.isEnabled();
- rule.modified = azr.isModified();
- rule.zenPolicy = azr.getZenPolicy();
- rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
- azr.getInterruptionFilter(), Global.ZEN_MODE_OFF);
- rule.configurationActivity = azr.getConfigurationActivity();
-
- if (isNew) {
- rule.id = ZenModeConfig.newRuleId();
- rule.creationTime = System.currentTimeMillis();
- rule.component = azr.getOwner();
- rule.pkg = pkg;
- }
-
- // Only the MODES_API path cares about the result, so just return whatever here.
- return true;
- }
+ return modified;
}
/**
@@ -1629,32 +1537,21 @@ public class ZenModeHelper {
}
private AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
- AutomaticZenRule azr;
- if (Flags.modesApi()) {
- azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId)
- .setManualInvocationAllowed(rule.allowManualInvocation)
- .setPackage(rule.pkg)
- .setCreationTime(rule.creationTime)
- .setIconResId(drawableResNameToResId(rule.pkg, rule.iconResName))
- .setType(rule.type)
- .setZenPolicy(rule.zenPolicy)
- .setDeviceEffects(rule.zenDeviceEffects)
- .setEnabled(rule.enabled)
- .setInterruptionFilter(
- NotificationManager.zenModeToInterruptionFilter(rule.zenMode))
- .setOwner(rule.component)
- .setConfigurationActivity(rule.configurationActivity)
- .setTriggerDescription(rule.triggerDescription)
- .build();
- } else {
- azr = new AutomaticZenRule(rule.name, rule.component,
- rule.configurationActivity,
- rule.conditionId, rule.zenPolicy,
- NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
- rule.enabled, rule.creationTime);
- azr.setPackageName(rule.pkg);
- }
- return azr;
+ return new AutomaticZenRule.Builder(rule.name, rule.conditionId)
+ .setManualInvocationAllowed(rule.allowManualInvocation)
+ .setPackage(rule.pkg)
+ .setCreationTime(rule.creationTime)
+ .setIconResId(drawableResNameToResId(rule.pkg, rule.iconResName))
+ .setType(rule.type)
+ .setZenPolicy(rule.zenPolicy)
+ .setDeviceEffects(rule.zenDeviceEffects)
+ .setEnabled(rule.enabled)
+ .setInterruptionFilter(
+ NotificationManager.zenModeToInterruptionFilter(rule.zenMode))
+ .setOwner(rule.component)
+ .setConfigurationActivity(rule.configurationActivity)
+ .setTriggerDescription(rule.triggerDescription)
+ .build();
}
// Update only the hasPriorityChannels state (aka areChannelsBypassingDnd) without modifying
@@ -1669,12 +1566,12 @@ public class ZenModeHelper {
if (config == null) return;
// If it already matches, do nothing
- if (config.areChannelsBypassingDnd == hasPriorityChannels) {
+ if (config.hasPriorityChannels == hasPriorityChannels) {
return;
}
ZenModeConfig newConfig = config.copy();
- newConfig.areChannelsBypassingDnd = hasPriorityChannels;
+ newConfig.hasPriorityChannels = hasPriorityChannels;
// The updated calculation of whether there are priority channels is always done by
// the system, even if the event causing the calculation had a different origin.
setConfigLocked(newConfig, null, ORIGIN_SYSTEM, "updateHasPriorityChannels",
@@ -1754,9 +1651,7 @@ public class ZenModeHelper {
newRule.zenMode = zenMode;
newRule.conditionId = conditionId;
newRule.enabler = caller;
- if (Flags.modesApi()) {
- newRule.allowManualInvocation = true;
- }
+ newRule.allowManualInvocation = true;
newConfig.manualRule = newRule;
}
}
@@ -1849,7 +1744,7 @@ public class ZenModeHelper {
boolean hasDefaultRules = config.automaticRules.containsAll(
ZenModeConfig.getDefaultRuleIds());
- long time = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis();
+ long time = mClock.millis();
if (config.automaticRules != null && config.automaticRules.size() > 0) {
for (ZenRule automaticRule : config.automaticRules.values()) {
if (forRestore) {
@@ -1863,7 +1758,7 @@ public class ZenModeHelper {
// Upon upgrading to a version with modes_api enabled, keep all behaviors of
// rules with null ZenPolicies explicitly as a copy of the global policy.
- if (Flags.modesApi() && config.version < ZenModeConfig.XML_VERSION_MODES_API) {
+ if (config.version < ZenModeConfig.XML_VERSION_MODES_API) {
// Keep the manual ("global") policy that from config.
ZenPolicy manualRulePolicy = config.getZenPolicy();
if (automaticRule.zenPolicy == null) {
@@ -1877,8 +1772,7 @@ public class ZenModeHelper {
}
}
- if (Flags.modesApi() && Flags.modesUi()
- && config.version < ZenModeConfig.XML_VERSION_MODES_UI) {
+ if (Flags.modesUi() && config.version < ZenModeConfig.XML_VERSION_MODES_UI) {
// Clear icons from implicit rules. App icons are not suitable for some
// surfaces, so juse use a default (the user can select a different one).
if (ZenModeConfig.isImplicitRuleId(automaticRule.id)) {
@@ -1904,11 +1798,11 @@ public class ZenModeHelper {
reason += ", reset to default rules";
}
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
SystemZenRules.maybeUpgradeRules(mContext, config);
}
- if (Flags.modesApi() && forRestore) {
+ if (forRestore) {
// Note: forBackup doesn't write deletedRules, but just in case.
config.deletedRules.clear();
}
@@ -1995,7 +1889,7 @@ public class ZenModeHelper {
if (config == null) return;
final ZenModeConfig newConfig = config.copy();
- if (Flags.modesApi() && !Flags.modesUi()) {
+ if (!Flags.modesUi()) {
// Fix for b/337193321 -- propagate changes to notificationPolicy to rules where
// the user cannot edit zen policy to emulate the previous "inheritance".
ZenPolicy previousPolicy = ZenAdapters.notificationPolicyToZenPolicy(
@@ -2026,6 +1920,7 @@ public class ZenModeHelper {
* <ul>
* <li>Rule instances whose owner is not installed.
* <li>Deleted rules that were deleted more than 30 days ago.
+ * <li>Implicit rules that haven't been used in 30 days (and have not been customized).
* </ul>
*/
private void cleanUpZenRules() {
@@ -2034,17 +1929,20 @@ public class ZenModeHelper {
final ZenModeConfig newConfig = mConfig.copy();
deleteRulesWithoutOwner(newConfig.automaticRules);
- if (Flags.modesApi()) {
- deleteRulesWithoutOwner(newConfig.deletedRules);
- for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) {
- ZenRule deletedRule = newConfig.deletedRules.valueAt(i);
- if (deletedRule.deletionInstant == null
- || deletedRule.deletionInstant.isBefore(keptRuleThreshold)) {
- newConfig.deletedRules.removeAt(i);
- }
+ deleteRulesWithoutOwner(newConfig.deletedRules);
+
+ for (int i = newConfig.deletedRules.size() - 1; i >= 0; i--) {
+ ZenRule deletedRule = newConfig.deletedRules.valueAt(i);
+ if (deletedRule.deletionInstant == null
+ || deletedRule.deletionInstant.isBefore(keptRuleThreshold)) {
+ newConfig.deletedRules.removeAt(i);
}
}
+ if (Flags.modesUi() && Flags.modesCleanupImplicit()) {
+ deleteUnusedImplicitRules(newConfig.automaticRules);
+ }
+
if (!newConfig.equals(mConfig)) {
setConfigLocked(newConfig, null, ORIGIN_SYSTEM,
"cleanUpZenRules", Process.SYSTEM_UID);
@@ -2053,7 +1951,7 @@ public class ZenModeHelper {
}
private void deleteRulesWithoutOwner(ArrayMap<String, ZenRule> ruleList) {
- long currentTime = Flags.modesApi() ? mClock.millis() : System.currentTimeMillis();
+ long currentTime = mClock.millis();
if (ruleList != null) {
for (int i = ruleList.size() - 1; i >= 0; i--) {
ZenRule rule = ruleList.valueAt(i);
@@ -2070,6 +1968,29 @@ public class ZenModeHelper {
}
}
+ private void deleteUnusedImplicitRules(ArrayMap<String, ZenRule> ruleList) {
+ if (ruleList == null) {
+ return;
+ }
+ Instant deleteIfUnusedSince = mClock.instant().minus(IMPLICIT_RULE_KEPT_FOR);
+
+ for (int i = ruleList.size() - 1; i >= 0; i--) {
+ ZenRule rule = ruleList.valueAt(i);
+ if (isImplicitRuleId(rule.id) && !rule.isUserModified()) {
+ if (rule.lastActivation == null) {
+ // This rule existed before we started tracking activation time. It *might* be
+ // in use. Set lastActivation=now so it has some time (IMPLICIT_RULE_KEPT_FOR)
+ // before being removed if truly unused.
+ rule.lastActivation = mClock.instant();
+ }
+
+ if (rule.lastActivation.isBefore(deleteIfUnusedSince)) {
+ ruleList.removeAt(i);
+ }
+ }
+ }
+ }
+
/**
* @return a copy of the zen mode configuration
*/
@@ -2188,7 +2109,7 @@ public class ZenModeHelper {
mZenMode, mConfig, mConsolidatedPolicy);
if (!config.equals(mConfig)) {
// Schedule broadcasts. Cannot be sent during boot, though.
- if (Flags.modesApi() && origin != ORIGIN_INIT) {
+ if (origin != ORIGIN_INIT) {
for (ZenRule rule : config.automaticRules.values()) {
ZenRule original = mConfig.automaticRules.get(rule.id);
if (original != null) {
@@ -2204,6 +2125,20 @@ public class ZenModeHelper {
}
}
+ // Update last activation for rules that are being activated.
+ if (Flags.modesUi() && Flags.modesCleanupImplicit()) {
+ Instant now = mClock.instant();
+ if (!mConfig.isManualActive() && config.isManualActive()) {
+ config.manualRule.lastActivation = now;
+ }
+ for (ZenRule rule : config.automaticRules.values()) {
+ ZenRule previousRule = mConfig.automaticRules.get(rule.id);
+ if (rule.isActive() && (previousRule == null || !previousRule.isActive())) {
+ rule.lastActivation = now;
+ }
+ }
+ }
+
mConfig = config;
dispatchOnConfigChanged();
updateAndApplyConsolidatedPolicyAndDeviceEffects(origin, reason);
@@ -2295,7 +2230,7 @@ public class ZenModeHelper {
private void applyCustomPolicy(ZenModeConfig config, ZenPolicy policy, ZenRule rule,
boolean useManualConfig) {
if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
policy.apply(ZenPolicy.getBasePolicyInterruptionFilterNone());
} else {
policy.apply(new ZenPolicy.Builder()
@@ -2304,7 +2239,7 @@ public class ZenModeHelper {
.build());
}
} else if (rule.zenMode == Global.ZEN_MODE_ALARMS) {
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
policy.apply(ZenPolicy.getBasePolicyInterruptionFilterAlarms());
} else {
policy.apply(new ZenPolicy.Builder()
@@ -2317,22 +2252,17 @@ public class ZenModeHelper {
} else if (rule.zenPolicy != null) {
policy.apply(rule.zenPolicy);
} else {
- if (Flags.modesApi()) {
- if (useManualConfig) {
- // manual rule is configured using the settings stored directly in ZenModeConfig
- policy.apply(config.getZenPolicy());
- } else {
- // under modes_api flag, an active automatic rule with no specified policy
- // inherits the device default settings as stored in mDefaultConfig. While the
- // rule's policy fields should be set upon creation, this is a fallback to
- // catch any that may have fallen through the cracks.
- Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule);
- policy.apply(Flags.modesUi()
- ? mDefaultConfig.getZenPolicy() : config.getZenPolicy());
- }
- } else {
- // active rule with no specified policy inherits the manual rule config settings
+ if (useManualConfig) {
+ // manual rule is configured using the settings stored directly in ZenModeConfig
policy.apply(config.getZenPolicy());
+ } else {
+ // An active automatic rule with no specified policy inherits the device default
+ // settings as stored in mDefaultConfig. While the rule's policy fields should be
+ // set upon creation, this is a fallback to catch any that may have fallen through
+ // the cracks.
+ Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule);
+ policy.apply(Flags.modesUi()
+ ? mDefaultConfig.getZenPolicy() : config.getZenPolicy());
}
}
}
@@ -2346,9 +2276,7 @@ public class ZenModeHelper {
ZenDeviceEffects.Builder deviceEffectsBuilder = new ZenDeviceEffects.Builder();
if (mConfig.isManualActive()) {
applyCustomPolicy(mConfig, policy, mConfig.manualRule, true);
- if (Flags.modesApi()) {
- deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects);
- }
+ deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects);
}
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -2356,12 +2284,10 @@ public class ZenModeHelper {
// Active rules with INTERRUPTION_FILTER_ALL are not included in consolidated
// policy. This is relevant in case some other active rule has a more
// restrictive INTERRUPTION_FILTER but a more lenient ZenPolicy!
- if (!Flags.modesApi() || automaticRule.zenMode != Global.ZEN_MODE_OFF) {
+ if (automaticRule.zenMode != Global.ZEN_MODE_OFF) {
applyCustomPolicy(mConfig, policy, automaticRule, false);
}
- if (Flags.modesApi()) {
- deviceEffectsBuilder.add(automaticRule.zenDeviceEffects);
- }
+ deviceEffectsBuilder.add(automaticRule.zenDeviceEffects);
}
}
@@ -2380,40 +2306,35 @@ public class ZenModeHelper {
ZenLog.traceSetConsolidatedZenPolicy(mConsolidatedPolicy, reason);
}
- if (Flags.modesApi()) {
- // Prevent other rules from applying grayscale if Driving is active (but allow it
- // if _Driving itself_ wants grayscale).
- if (Flags.modesUi() && preventZenDeviceEffectsWhileDriving()) {
- boolean hasActiveDriving = false;
- boolean hasActiveDrivingWithGrayscale = false;
- for (ZenRule rule : mConfig.automaticRules.values()) {
- if (rule.isActive() && rule.type == TYPE_DRIVING) {
- hasActiveDriving = true;
- if (rule.zenDeviceEffects != null
- && rule.zenDeviceEffects.shouldDisplayGrayscale()) {
- hasActiveDrivingWithGrayscale = true;
- break; // Further rules won't affect decision.
- }
+ // Prevent other rules from applying grayscale if Driving is active (but allow it
+ // if _Driving itself_ wants grayscale).
+ if (Flags.modesUi() && preventZenDeviceEffectsWhileDriving()) {
+ boolean hasActiveDriving = false;
+ boolean hasActiveDrivingWithGrayscale = false;
+ for (ZenRule rule : mConfig.automaticRules.values()) {
+ if (rule.isActive() && rule.type == TYPE_DRIVING) {
+ hasActiveDriving = true;
+ if (rule.zenDeviceEffects != null
+ && rule.zenDeviceEffects.shouldDisplayGrayscale()) {
+ hasActiveDrivingWithGrayscale = true;
+ break; // Further rules won't affect decision.
}
}
- if (hasActiveDriving && !hasActiveDrivingWithGrayscale) {
- deviceEffectsBuilder.setShouldDisplayGrayscale(false);
- }
}
-
- ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
- if (!deviceEffects.equals(mConsolidatedDeviceEffects)) {
- mConsolidatedDeviceEffects = deviceEffects;
- mHandler.postApplyDeviceEffects(origin);
+ if (hasActiveDriving && !hasActiveDrivingWithGrayscale) {
+ deviceEffectsBuilder.setShouldDisplayGrayscale(false);
}
}
+
+ ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
+ if (!deviceEffects.equals(mConsolidatedDeviceEffects)) {
+ mConsolidatedDeviceEffects = deviceEffects;
+ mHandler.postApplyDeviceEffects(origin);
+ }
}
}
private void applyConsolidatedDeviceEffects(@ConfigOrigin int source) {
- if (!Flags.modesApi()) {
- return;
- }
DeviceEffectsApplier applier;
ZenDeviceEffects effects;
synchronized (mConfigLock) {
@@ -2434,10 +2355,8 @@ public class ZenModeHelper {
* to the current locale.
*/
private static void updateDefaultConfig(Context context, ZenModeConfig defaultConfig) {
- if (Flags.modesApi()) {
- updateDefaultAutomaticRulePolicies(defaultConfig);
- }
- if (Flags.modesApi() && Flags.modesUi()) {
+ updateDefaultAutomaticRulePolicies(defaultConfig);
+ if (Flags.modesUi()) {
SystemZenRules.maybeUpgradeRules(context, defaultConfig);
}
updateRuleStringsForCurrentLocale(context, defaultConfig);
@@ -2453,7 +2372,7 @@ public class ZenModeHelper {
rule.name = context.getResources()
.getString(R.string.zen_mode_default_every_night_name);
}
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
SystemZenRules.updateTriggerDescription(context, rule);
}
}
@@ -2462,10 +2381,6 @@ public class ZenModeHelper {
// Updates the policies in the default automatic rules (provided via default XML config) to
// be fully filled in default values.
private static void updateDefaultAutomaticRulePolicies(ZenModeConfig defaultConfig) {
- if (!Flags.modesApi()) {
- // Should be checked before calling, but just in case.
- return;
- }
ZenPolicy defaultPolicy = defaultConfig.getZenPolicy();
for (ZenRule rule : defaultConfig.automaticRules.values()) {
if (ZenModeConfig.getDefaultRuleIds().contains(rule.id) && rule.zenPolicy == null) {
@@ -2611,6 +2526,7 @@ public class ZenModeHelper {
}
}
+ // TODO: b/368247671 - Delete this method AND default_zen_mode_config.xml when inlining modes_ui
private ZenModeConfig readDefaultConfig(Resources resources) {
XmlResourceParser parser = null;
try {
@@ -2649,7 +2565,7 @@ public class ZenModeHelper {
events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
/* optional int32 user = 1 */ user,
/* optional bool enabled = 2 */ config.isManualActive(),
- /* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd,
+ /* optional bool channels_bypassing = 3 */ config.hasPriorityChannels,
/* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG,
/* optional string id = 5 */ "", // empty for root config
/* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config
@@ -2924,9 +2840,6 @@ public class ZenModeHelper {
* ({@link #addAutomaticZenRule}, {@link #removeAutomaticZenRule}, etc, makes sense.
*/
private static void checkManageRuleOrigin(String method, @ConfigOrigin int origin) {
- if (!Flags.modesApi()) {
- return;
- }
checkArgument(origin == ORIGIN_APP || origin == ORIGIN_SYSTEM
|| origin == ORIGIN_USER_IN_SYSTEMUI,
"Expected one of ORIGIN_APP, ORIGIN_SYSTEM, or "
@@ -2939,9 +2852,6 @@ public class ZenModeHelper {
* {@link #setAutomaticZenRuleStateFromConditionProvider} makes sense.
*/
private static void checkSetRuleStateOrigin(String method, @ConfigOrigin int origin) {
- if (!Flags.modesApi()) {
- return;
- }
checkArgument(origin == ORIGIN_APP || origin == ORIGIN_USER_IN_APP
|| origin == ORIGIN_SYSTEM || origin == ORIGIN_USER_IN_SYSTEMUI,
"Expected one of ORIGIN_APP, ORIGIN_USER_IN_APP, ORIGIN_SYSTEM, or "
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 048f2b6b0cbc..76cd5c88b388 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -210,3 +210,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "managed_services_concurrent_multiuser"
+ namespace: "systemui"
+ description: "Enables ManagedServices to support Concurrent multi user environment"
+ bug: "380297485"
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index d538bb876b64..c3af578de369 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -176,16 +176,13 @@ public class BackgroundInstallControlService extends SystemService {
if (Flags.bicClient()) {
mService.enforceCallerPermissions();
}
- if (!Build.IS_DEBUGGABLE) {
- return mService.getBackgroundInstalledPackages(flags, userId);
- }
// The debug.transparency.bg-install-apps (only works for debuggable builds)
// is used to set mock list of background installed apps for testing.
// The list of apps' names is delimited by ",".
// TODO: Remove after migrating test to new background install method using
// {@link BackgroundInstallControlCallbackHelperTest}.installPackage b/310983905
String propertyString = SystemProperties.get("debug.transparency.bg-install-apps");
- if (TextUtils.isEmpty(propertyString)) {
+ if (TextUtils.isEmpty(propertyString) || !Build.IS_DEBUGGABLE) {
return mService.getBackgroundInstalledPackages(flags, userId);
} else {
return mService.getMockBackgroundInstalledPackages(propertyString);
@@ -219,10 +216,27 @@ public class BackgroundInstallControlService extends SystemService {
PackageManager.PackageInfoFlags.of(flags), userId);
initBackgroundInstalledPackages();
+ if(Build.IS_DEBUGGABLE) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Tracked background installed package size: ")
+ .append(mBackgroundInstalledPackages.size())
+ .append("\n");
+ for (int i = 0; i < mBackgroundInstalledPackages.size(); ++i) {
+ int installingUserId = mBackgroundInstalledPackages.keyAt(i);
+ mBackgroundInstalledPackages.get(installingUserId).forEach(pkgName ->
+ sb.append("userId: ").append(installingUserId)
+ .append(", name: ").append(pkgName).append("\n"));
+ }
+ Slog.d(TAG, "Tracked background installed package: " + sb.toString());
+ }
+
ListIterator<PackageInfo> iter = packages.listIterator();
while (iter.hasNext()) {
String packageName = iter.next().packageName;
if (!mBackgroundInstalledPackages.contains(userId, packageName)) {
+ if(Build.IS_DEBUGGABLE) {
+ Slog.d(TAG, packageName + " is not tracked, removing");
+ }
iter.remove();
}
}
@@ -284,6 +298,9 @@ public class BackgroundInstallControlService extends SystemService {
}
void handlePackageAdd(String packageName, int userId) {
+ if(Build.IS_DEBUGGABLE) {
+ Slog.d(TAG, "handlePackageAdd: checking " + packageName);
+ }
ApplicationInfo appInfo = null;
try {
appInfo =
@@ -302,7 +319,7 @@ public class BackgroundInstallControlService extends SystemService {
installerPackageName = installInfo.getInstallingPackageName();
initiatingPackageName = installInfo.getInitiatingPackageName();
} catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Package's installer not found " + packageName);
+ Slog.w(TAG, "Package's installer not found: " + packageName);
return;
}
@@ -314,6 +331,10 @@ public class BackgroundInstallControlService extends SystemService {
VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT,
userId)
!= PERMISSION_GRANTED) {
+ if(Build.IS_DEBUGGABLE) {
+ Slog.d(TAG, "handlePackageAdd " + packageName + ": installer doesn't "
+ + "have INSTALL_PACKAGES permission, skipping");
+ }
return;
}
@@ -324,6 +345,10 @@ public class BackgroundInstallControlService extends SystemService {
if (installedByAdb(initiatingPackageName)
|| wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
+ if(Build.IS_DEBUGGABLE) {
+ Slog.d(TAG, "handlePackageAdd " + packageName + ": is installed by ADB or was "
+ + "foreground installation, skipping");
+ }
return;
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 15688c0f7366..28117470e7a3 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -204,6 +204,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -1023,6 +1024,7 @@ final class InstallPackageHelper {
*/
void installPackagesTraced(List<InstallRequest> requests, MoveInfo moveInfo) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+ boolean pendingForDexopt = false;
boolean success = false;
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
@@ -1036,17 +1038,41 @@ final class InstallPackageHelper {
if (reconciledPackages == null) {
return;
}
+
if (renameAndUpdatePaths(requests)) {
// rename before dexopt because art will encoded the path in the odex/vdex file
if (Flags.improveInstallFreeze()) {
- prepPerformDexoptIfNeeded(reconciledPackages);
- }
- if (commitInstallPackages(reconciledPackages)) {
- success = true;
+ pendingForDexopt = true;
+ final Runnable actionsAfterDexopt = () ->
+ doPostDexopt(reconciledPackages, requests,
+ createdAppId, moveInfo, acquireTime);
+ prepPerformDexoptIfNeeded(reconciledPackages, actionsAfterDexopt);
+ } else {
+ if (commitInstallPackages(reconciledPackages)) {
+ success = true;
+ }
}
}
}
} finally {
+ if (!pendingForDexopt) {
+ completeInstallProcess(requests, createdAppId, success);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ doPostInstall(requests, moveInfo);
+ releaseWakeLock(acquireTime, requests.size());
+ }
+ }
+ }
+
+ void doPostDexopt(List<ReconciledPackage> reconciledPackages,
+ List<InstallRequest> requests, Map<String, Boolean> createdAppId,
+ MoveInfo moveInfo, long acquireTime) {
+ boolean success = false;
+ try {
+ if (commitInstallPackages(reconciledPackages)) {
+ success = true;
+ }
+ } finally {
completeInstallProcess(requests, createdAppId, success);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
doPostInstall(requests, moveInfo);
@@ -1123,7 +1149,7 @@ final class InstallPackageHelper {
throws PackageManagerException {
final int userId = installRequest.getUserId();
if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT
- && !mPm.mUserManager.exists(userId)) {
+ && !ArrayUtils.contains(allUsers, userId)) {
throw new PackageManagerException(PackageManagerException.INTERNAL_ERROR_MISSING_USER,
"User " + userId + " doesn't exist or has been removed");
}
@@ -1155,7 +1181,9 @@ final class InstallPackageHelper {
}
}
- private void prepPerformDexoptIfNeeded(List<ReconciledPackage> reconciledPackages) {
+ private void prepPerformDexoptIfNeeded(List<ReconciledPackage> reconciledPackages,
+ Runnable actionsAfterDexopt) {
+ List<CompletableFuture<Void>> completableFutures = new ArrayList<>();
for (ReconciledPackage reconciledPkg : reconciledPackages) {
final InstallRequest request = reconciledPkg.mInstallRequest;
// prepare profiles
@@ -1171,6 +1199,7 @@ final class InstallPackageHelper {
mSharedLibraries.executeSharedLibrariesUpdate(request.getParsedPackage(), ps,
null, null, reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
}
+
try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
final int[] newUsers = getNewUsers(request, allUsers);
// Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
@@ -1182,11 +1211,22 @@ final class InstallPackageHelper {
}
} catch (PackageManagerException e) {
request.setError(e.error, e.getMessage());
- return;
+ break;
}
request.setKeepArtProfile(true);
- // TODO(b/388159696): Use performDexoptIfNeededAsync.
- DexOptHelper.performDexoptIfNeeded(request, mDexManager, null /* installLock */);
+
+ CompletableFuture<Void> future =
+ DexOptHelper.performDexoptIfNeededAsync(request, mDexManager);
+ completableFutures.add(future);
+ }
+
+ if (!completableFutures.isEmpty()) {
+ CompletableFuture<Void> allFutures =
+ CompletableFuture.allOf(
+ completableFutures.toArray(CompletableFuture[]::new));
+ var unused = allFutures.thenRun(() -> mPm.mHandler.post(actionsAfterDexopt));
+ } else {
+ actionsAfterDexopt.run();
}
}
@@ -2759,6 +2799,7 @@ final class InstallPackageHelper {
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
+ // run synchronous dexopt if the freeze improvement is not supported
DexOptHelper.performDexoptIfNeeded(
installRequest, mDexManager, mPm.mInstallLock.getRawLock());
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index e1fcc6650650..2d0bb258e89f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -805,22 +805,20 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
- if (Flags.recoverabilityDetection()) {
- if (params.rollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH
- || params.rollbackImpactLevel
- == PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL) {
- if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
- throw new IllegalArgumentException(
- "Can't set rollbackImpactLevel when rollback is not enabled");
- }
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Setting rollbackImpactLevel requires the MANAGE_ROLLBACKS permission");
- }
- } else if (params.rollbackImpactLevel < 0) {
- throw new IllegalArgumentException("rollbackImpactLevel can't be negative.");
+ if (params.rollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH
+ || params.rollbackImpactLevel
+ == PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL) {
+ if ((params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
+ throw new IllegalArgumentException(
+ "Can't set rollbackImpactLevel when rollback is not enabled");
+ }
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Setting rollbackImpactLevel requires the MANAGE_ROLLBACKS permission");
}
+ } else if (params.rollbackImpactLevel < 0) {
+ throw new IllegalArgumentException("rollbackImpactLevel can't be negative.");
}
boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 352985d5a023..136cb1259113 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -948,6 +948,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
break;
case MSG_ON_NATIVE_LIBS_EXTRACTED:
handleOnNativeLibsExtracted();
+ break;
}
return true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index aa235c2258ac..cf598e89c988 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3561,9 +3561,6 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.setEnableRollback(true, rollbackStrategy);
break;
case "--rollback-impact-level":
- if (!Flags.recoverabilityDetection()) {
- throw new IllegalArgumentException("Unknown option " + opt);
- }
int rollbackImpactLevel = Integer.parseInt(peekNextArg());
if (rollbackImpactLevel < PackageManager.ROLLBACK_USER_IMPACT_LOW
|| rollbackImpactLevel
@@ -4775,11 +4772,9 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --full: cause the app to be installed as a non-ephemeral full app");
pw.println(" --enable-rollback: enable rollbacks for the upgrade.");
pw.println(" 0=restore (default), 1=wipe, 2=retain");
- if (Flags.recoverabilityDetection()) {
- pw.println(
- " --rollback-impact-level: set device impact required for rollback.");
- pw.println(" 0=low (default), 1=high, 2=manual only");
- }
+ pw.println(
+ " --rollback-impact-level: set device impact required for rollback.");
+ pw.println(" 0=low (default), 1=high, 2=manual only");
pw.println(" --install-location: force the install location:");
pw.println(" 0=auto, 1=internal only, 2=prefer external");
pw.println(" --install-reason: indicates why the app is being installed:");
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 045d4db0a1f1..346327d1fa74 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -453,7 +453,7 @@ class ShortcutLauncher extends ShortcutPackageItem {
@Override
protected File getShortcutPackageItemFile() {
final File path = new File(mShortcutUser.mService.injectUserDataPath(
- mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LUANCHERS);
+ mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LAUNCHERS);
// Package user id and owner id can have different values for ShortcutLaunchers. Adding
// user Id to the file name to create a unique path. Owner id is used in the root path.
final String fileName = getPackageName() + getPackageUserId() + ".xml";
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 373c1ed3c386..d3513053caf3 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -16,7 +16,9 @@
package com.android.server.pm;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
+
+import static com.android.server.pm.ShortcutUser.DIRECTORY_LAUNCHERS;
+import static com.android.server.pm.ShortcutUser.DIRECTORY_PACKAGES;
import android.Manifest.permission;
import android.annotation.IntDef;
@@ -94,7 +96,6 @@ import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArraySet;
@@ -112,7 +113,6 @@ import android.view.IWindowManager;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
@@ -155,7 +155,6 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
/**
* TODO:
@@ -171,7 +170,7 @@ public class ShortcutService extends IShortcutService.Stub {
static final boolean DEBUG = false; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
- static final boolean DEBUG_REBOOT = Build.IS_DEBUGGABLE;
+ static final boolean DEBUG_REBOOT = false; // STOPSHIP if true
@VisibleForTesting
static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -292,7 +291,8 @@ public class ShortcutService extends IShortcutService.Stub {
final Context mContext;
- private final Object mServiceLock = new Object();
+ @VisibleForTesting
+ final Object mServiceLock = new Object();
private final Object mNonPersistentUsersLock = new Object();
private final Object mWtfLock = new Object();
@@ -982,7 +982,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
@VisibleForTesting
- void saveBaseState() {
+ void injectSaveBaseState() {
try (ResilientAtomicFile file = getBaseStateFile()) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Saving to " + file.getBaseFile());
@@ -994,18 +994,7 @@ public class ShortcutService extends IShortcutService.Stub {
outs = file.startWrite();
}
- // Write to XML
- TypedXmlSerializer out = Xml.resolveSerializer(outs);
- out.startDocument(null, true);
- out.startTag(null, TAG_ROOT);
-
- // Body.
- // No locking required. Ok to add lock later if we save more data.
- writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime.get());
-
- // Epilogue.
- out.endTag(null, TAG_ROOT);
- out.endDocument();
+ saveBaseStateAsXml(outs);
// Close.
injectFinishWrite(file, outs);
@@ -1016,10 +1005,32 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
+ @VisibleForTesting
+ protected void saveBaseStateAsXml(OutputStream outs) throws IOException {
+ // Write to XML
+ TypedXmlSerializer out = Xml.resolveSerializer(outs);
+ out.startDocument(null, true);
+ out.startTag(null, TAG_ROOT);
+
+ // Body.
+ // No locking required. Ok to add lock later if we save more data.
+ writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime.get());
+
+ // Epilogue.
+ out.endTag(null, TAG_ROOT);
+ out.endDocument();
+ }
+
@GuardedBy("mServiceLock")
private void loadBaseStateLocked() {
mRawLastResetTime.set(0);
+ injectLoadBaseState();
+ // Adjust the last reset time.
+ getLastResetTimeLocked();
+ }
+ @VisibleForTesting
+ protected void injectLoadBaseState() {
try (ResilientAtomicFile file = getBaseStateFile()) {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Loading from " + file.getBaseFile());
@@ -1030,34 +1041,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (in == null) {
throw new FileNotFoundException(file.getBaseFile().getAbsolutePath());
}
-
- TypedXmlPullParser parser = Xml.resolvePullParser(in);
-
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final int depth = parser.getDepth();
- // Check the root tag
- final String tag = parser.getName();
- if (depth == 1) {
- if (!TAG_ROOT.equals(tag)) {
- Slog.v(TAG, "Invalid root tag: " + tag);
- return;
- }
- continue;
- }
- // Assume depth == 2
- switch (tag) {
- case TAG_LAST_RESET_TIME:
- mRawLastResetTime.set(parseLongAttribute(parser, ATTR_VALUE));
- break;
- default:
- Slog.v(TAG, "Invalid tag: " + tag);
- break;
- }
- }
+ loadBaseStateAsXml(in);
} catch (FileNotFoundException e) {
// Use the default
} catch (IOException | XmlPullParserException e) {
@@ -1067,8 +1051,38 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
}
- // Adjust the last reset time.
- getLastResetTimeLocked();
+ }
+
+ @VisibleForTesting
+ protected void loadBaseStateAsXml(InputStream in)
+ throws IOException, XmlPullParserException {
+ TypedXmlPullParser parser = Xml.resolvePullParser(in);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ // Check the root tag
+ final String tag = parser.getName();
+ if (depth == 1) {
+ if (!TAG_ROOT.equals(tag)) {
+ Slog.v(TAG, "Invalid root tag: " + tag);
+ return;
+ }
+ continue;
+ }
+ // Assume depth == 2
+ switch (tag) {
+ case TAG_LAST_RESET_TIME:
+ mRawLastResetTime.set(parseLongAttribute(parser, ATTR_VALUE));
+ break;
+ default:
+ Slog.v(TAG, "Invalid tag: " + tag);
+ break;
+ }
+ }
}
@VisibleForTesting
@@ -1083,7 +1097,8 @@ public class ShortcutService extends IShortcutService.Stub {
"user shortcut", null);
}
- private void saveUser(@UserIdInt int userId) {
+ @VisibleForTesting
+ protected void injectSaveUser(@UserIdInt int userId) {
try (ResilientAtomicFile file = getUserFile(userId)) {
FileOutputStream os = null;
try {
@@ -1092,8 +1107,14 @@ public class ShortcutService extends IShortcutService.Stub {
}
synchronized (mServiceLock) {
+ // Since we are not handling package deletion yet, or any single package
+ // changes, just clean the directory and rewrite all the ShortcutPackageItems.
+ final File root = injectUserDataPath(userId);
+ FileUtils.deleteContents(new File(root, DIRECTORY_PACKAGES));
+ FileUtils.deleteContents(new File(root, DIRECTORY_LAUNCHERS));
os = file.startWrite();
saveUserInternalLocked(userId, os, /* forBackup= */ false);
+ getUserShortcutsLocked(userId).scheduleSaveAllLaunchersAndPackages();
}
injectFinishWrite(file, os);
@@ -1109,8 +1130,9 @@ public class ShortcutService extends IShortcutService.Stub {
getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger);
}
+ @VisibleForTesting
@GuardedBy("mServiceLock")
- private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
+ protected void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
boolean forBackup) throws IOException, XmlPullParserException {
// Write to XML
@@ -1138,8 +1160,9 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
+ @VisibleForTesting
@Nullable
- private ShortcutUser loadUserLocked(@UserIdInt int userId) {
+ protected ShortcutUser injectLoadUserLocked(@UserIdInt int userId) {
try (ResilientAtomicFile file = getUserFile(userId)) {
FileInputStream in = null;
try {
@@ -1157,12 +1180,13 @@ public class ShortcutService extends IShortcutService.Stub {
} catch (Exception e) {
// Remove corrupted file and retry.
file.failRead(in, e);
- return loadUserLocked(userId);
+ return injectLoadUserLocked(userId);
}
}
}
- private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
+ @VisibleForTesting
+ protected ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
boolean fromBackup) throws XmlPullParserException, IOException,
InvalidFileFormatException {
@@ -1240,9 +1264,9 @@ public class ShortcutService extends IShortcutService.Stub {
for (int i = dirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = dirtyUserIds.get(i);
if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
- saveBaseState();
+ injectSaveBaseState();
} else {
- saveUser(userId);
+ injectSaveUser(userId);
}
}
} catch (Exception e) {
@@ -1349,7 +1373,7 @@ public class ShortcutService extends IShortcutService.Stub {
ShortcutUser userPackages = mUsers.get(userId);
if (userPackages == null) {
- userPackages = loadUserLocked(userId);
+ userPackages = injectLoadUserLocked(userId);
if (userPackages == null) {
userPackages = new ShortcutUser(this, userId);
}
@@ -1430,8 +1454,9 @@ public class ShortcutService extends IShortcutService.Stub {
* {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
* saves are going on.
*/
+ @VisibleForTesting
@GuardedBy("mServiceLock")
- private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
+ void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
}
@@ -2755,7 +2780,7 @@ public class ShortcutService extends IShortcutService.Stub {
getPackageShortcutsLocked(packageName, userId)
.resetRateLimitingForCommandLineNoSaving();
}
- saveUser(userId);
+ injectSaveUser(userId);
}
// We override this method in unit tests to do a simpler check.
@@ -4407,7 +4432,7 @@ public class ShortcutService extends IShortcutService.Stub {
pw.println();
});
}
- saveUser(userId);
+ injectSaveUser(userId);
}
// === Dump ===
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index bc8cc7ba29af..632fd16a32f7 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -21,16 +21,12 @@ import android.annotation.UserIdInt;
import android.content.pm.ShortcutManager;
import android.content.pm.UserPackage;
import android.metrics.LogMaker;
-import android.os.Binder;
-import android.os.FileUtils;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -49,8 +45,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -63,7 +57,7 @@ class ShortcutUser {
private static final String TAG = ShortcutService.TAG;
static final String DIRECTORY_PACKAGES = "packages";
- static final String DIRECTORY_LUANCHERS = "launchers";
+ static final String DIRECTORY_LAUNCHERS = "launchers";
static final String TAG_ROOT = "user";
private static final String TAG_LAUNCHER = "launcher";
@@ -322,41 +316,43 @@ class ShortcutUser {
mService.injectBuildFingerprint());
}
- if (!forBackup) {
- // Since we are not handling package deletion yet, or any single package changes, just
- // clean the directory and rewrite all the ShortcutPackageItems.
- final File root = mService.injectUserDataPath(mUserId);
- FileUtils.deleteContents(new File(root, DIRECTORY_PACKAGES));
- FileUtils.deleteContents(new File(root, DIRECTORY_LUANCHERS));
- }
// Can't use forEachPackageItem due to the checked exceptions.
+ if (forBackup) {
+ int size = mLaunchers.size();
+ for (int i = 0; i < size; i++) {
+ saveShortcutPackageItem(out, mLaunchers.valueAt(i));
+ }
+ size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ saveShortcutPackageItem(out, mPackages.valueAt(i));
+ }
+ }
+
+ out.endTag(null, TAG_ROOT);
+ }
+
+ void scheduleSaveAllLaunchersAndPackages() {
{
final int size = mLaunchers.size();
for (int i = 0; i < size; i++) {
- saveShortcutPackageItem(out, mLaunchers.valueAt(i), forBackup);
+ mLaunchers.valueAt(i).scheduleSave();
}
}
{
final int size = mPackages.size();
for (int i = 0; i < size; i++) {
- saveShortcutPackageItem(out, mPackages.valueAt(i), forBackup);
+ mPackages.valueAt(i).scheduleSave();
}
}
-
- out.endTag(null, TAG_ROOT);
}
- private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi,
- boolean forBackup) throws IOException, XmlPullParserException {
- if (forBackup) {
- if (spi.getPackageUserId() != spi.getOwnerUserId()) {
- return; // Don't save cross-user information.
- }
- spi.waitForBitmapSaves();
- spi.saveToXml(out, forBackup);
- } else {
- spi.scheduleSave();
+ private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi)
+ throws IOException, XmlPullParserException {
+ if (spi.getPackageUserId() != spi.getOwnerUserId()) {
+ return; // Don't save cross-user information.
}
+ spi.waitForBitmapSaves();
+ spi.saveToXml(out, true /* forBackup */);
}
public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId,
@@ -429,7 +425,7 @@ class ShortcutUser {
}
});
- forMainFilesIn(new File(root, DIRECTORY_LUANCHERS), (File f) -> {
+ forMainFilesIn(new File(root, DIRECTORY_LAUNCHERS), (File f) -> {
final ShortcutLauncher sl =
ShortcutLauncher.loadFromFile(f, ret, userId, fromBackup);
if (sl != null) {
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index dd60a155f2fb..8510ee70cc56 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -179,8 +179,7 @@ final class VerifyingSession {
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!mOriginInfo.mExisting) {
- final boolean verifyForRollback = Flags.recoverabilityDetection()
- ? !isARollback() : true;
+ final boolean verifyForRollback = !isARollback();
if (!isApex() && !isArchivedInstallation() && verifyForRollback) {
// TODO(b/182426975): treat APEX as APK when APK verification is concerned
sendApkVerificationRequest(pkgLite);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 44d787f790cf..c31c287017c3 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -82,12 +82,10 @@ import android.util.LongSparseLongArray;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.infra.AndroidFuture;
-import com.android.internal.policy.AttributeCache;
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
@@ -167,6 +165,7 @@ public final class PermissionPolicyService extends SystemService {
private Context mContext;
private PackageManagerInternal mPackageManagerInternal;
private PermissionManagerServiceInternal mPermissionManagerInternal;
+ private ActivityTaskManagerInternal mActivityTaskManagerInternal;
private NotificationManagerInternal mNotificationManager;
private TelephonyManager mTelephonyManager;
private final KeyguardManager mKeyguardManager;
@@ -189,6 +188,7 @@ public final class PermissionPolicyService extends SystemService {
PackageManagerInternal.class);
mPermissionManagerInternal = LocalServices.getService(
PermissionManagerServiceInternal.class);
+ mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -1154,7 +1154,7 @@ public final class PermissionPolicyService extends SystemService {
activityInfo.packageName, info.getCallingPackage(),
info.getIntent(), info.getCheckedOptions(), activityInfo.name,
true)
- || isNoDisplayActivity(activityInfo)) {
+ || isNoDisplayActivity(activityInfo, info.getUserId())) {
return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
@@ -1170,9 +1170,7 @@ public final class PermissionPolicyService extends SystemService {
};
private void onActivityManagerReady() {
- ActivityTaskManagerInternal atm =
- LocalServices.getService(ActivityTaskManagerInternal.class);
- atm.registerActivityStartInterceptor(
+ mActivityTaskManagerInternal.registerActivityStartInterceptor(
ActivityInterceptorCallback.PERMISSION_POLICY_ORDERED_ID,
mActivityInterceptorCallback);
}
@@ -1227,20 +1225,14 @@ public final class PermissionPolicyService extends SystemService {
null, activityName, false);
}
- private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo) {
+ private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo, int userId) {
final int themeResource = aInfo.getThemeResource();
if (themeResource == Resources.ID_NULL) {
return false;
}
- boolean noDisplay = false;
- final AttributeCache.Entry ent = AttributeCache.instance()
- .get(aInfo.packageName, themeResource, R.styleable.Window, 0);
- if (ent != null) {
- noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
- }
-
- return noDisplay;
+ return mActivityTaskManagerInternal.isNoDisplay(aInfo.packageName, themeResource,
+ userId);
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4153cd1be0a6..980fb155999e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1168,8 +1168,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mContext.getContentResolver(), Settings.Secure.GLANCEABLE_HUB_ENABLED,
1, mCurrentUserId) == 1;
- return mUserManagerInternal.isUserUnlocked(mCurrentUserId) && hubEnabled
- && mDreamManagerInternal.dreamConditionActive();
+ return mUserManagerInternal != null && mUserManagerInternal.isUserUnlocked(mCurrentUserId)
+ && hubEnabled && mDreamManagerInternal.dreamConditionActive();
}
@VisibleForTesting
@@ -1260,7 +1260,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mContext.getContentResolver(), Settings.Secure.GLANCEABLE_HUB_ENABLED,
1, mCurrentUserId) == 1;
- if (mDreamManagerInternal.isDreaming() || isKeyguardShowing()) {
+ if ((mDreamManagerInternal != null && mDreamManagerInternal.isDreaming())
+ || isKeyguardShowing()) {
// If the device is already dreaming or on keyguard, go to sleep.
sleepDefaultDisplayFromPowerButton(eventTime, 0);
break;
@@ -2339,6 +2340,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowWakeUpPolicy getWindowWakeUpPolicy() {
return new WindowWakeUpPolicy(mContext);
}
+
+ DreamManagerInternal getDreamManagerInternal() {
+ return LocalServices.getService(DreamManagerInternal.class);
+ }
}
/** {@inheritDoc} */
@@ -2357,7 +2362,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mInputManager = mContext.getSystemService(InputManager.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
- mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
+ mDreamManagerInternal = injector.getDreamManagerInternal();
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
@@ -3713,15 +3718,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case KeyEvent.KEYCODE_N:
if (firstDown && event.isMetaPressed()) {
- if (event.isCtrlPressed()) {
- sendSystemKeyToStatusBarAsync(event);
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES);
- } else {
- toggleNotificationPanel();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
- }
+ toggleNotificationPanel();
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
return true;
}
break;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8fae875eb29b..e3eced252d1f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3379,7 +3379,7 @@ public final class PowerManagerService extends SystemService
}
changed = sleepPowerGroupLocked(powerGroup, time,
PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, Process.SYSTEM_UID);
- } else if (shouldNapAtBedTimeLocked()) {
+ } else if (shouldNapAtBedTimeLocked(powerGroup)) {
changed = dreamPowerGroupLocked(powerGroup, time,
Process.SYSTEM_UID, /* allowWake= */ false);
} else {
@@ -3395,7 +3395,10 @@ public final class PowerManagerService extends SystemService
* activity timeout has expired and it's bedtime.
*/
@GuardedBy("mLock")
- private boolean shouldNapAtBedTimeLocked() {
+ private boolean shouldNapAtBedTimeLocked(PowerGroup powerGroup) {
+ if (!powerGroup.supportsSandmanLocked()) {
+ return false;
+ }
return mDreamsActivateOnSleepSetting
|| (mDreamsActivateOnDockSetting
&& mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED)
@@ -3617,9 +3620,10 @@ public final class PowerManagerService extends SystemService
if (!mDreamsDisabledByAmbientModeSuppressionConfig) {
return;
}
+ final PowerGroup defaultPowerGroup = mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP);
if (!isSuppressed && mIsPowered && mDreamsSupportedConfig && mDreamsEnabledSetting
- && shouldNapAtBedTimeLocked() && isItBedTimeYetLocked(
- mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP))) {
+ && shouldNapAtBedTimeLocked(defaultPowerGroup)
+ && isItBedTimeYetLocked(defaultPowerGroup)) {
napInternal(SystemClock.uptimeMillis(), Process.SYSTEM_UID, /* allowWake= */ true);
} else if (isSuppressed) {
mDirty |= DIRTY_SETTINGS;
diff --git a/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
index 6f6a7ff5064a..ca2237562fe1 100644
--- a/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
@@ -29,7 +29,8 @@ import com.android.internal.os.PowerStats;
public class ScreenPowerStatsLayout extends PowerStatsLayout {
private static final String EXTRA_DEVICE_SCREEN_COUNT = "dsc";
private static final String EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION = "dsd";
- private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbd";
+ private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbds";
+ private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS_COMPAT = "dbd";
private static final String EXTRA_DEVICE_DOZE_DURATION_POSITION = "ddd";
private static final String EXTRA_DEVICE_DOZE_POWER_POSITION = "ddp";
private static final String EXTRA_UID_FOREGROUND_DURATION = "uf";
@@ -55,8 +56,14 @@ public class ScreenPowerStatsLayout extends PowerStatsLayout {
PersistableBundle extras = descriptor.extras;
mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
- mDeviceBrightnessDurationPositions = extras.getIntArray(
+ mDeviceBrightnessDurationPositions = getIntArray(extras,
EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
+ if (mDeviceBrightnessDurationPositions == null) {
+ // We should never put arrays in PowerStats.Descriptor because of the performance of
+ // .equals
+ mDeviceBrightnessDurationPositions = extras.getIntArray(
+ EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS_COMPAT);
+ }
mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION);
mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
@@ -67,7 +74,7 @@ public class ScreenPowerStatsLayout extends PowerStatsLayout {
super.toExtras(extras);
extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
- extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
+ putIntArray(extras, EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
mDeviceBrightnessDurationPositions);
extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition);
diff --git a/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
index e8df3ddfe5e8..c382534ac433 100644
--- a/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
@@ -27,8 +27,10 @@ import java.util.Map;
public class SensorPowerStatsLayout extends PowerStatsLayout {
private static final String TAG = "SensorPowerStatsLayout";
- private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh";
+ private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dshs";
+ private static final String EXTRA_DEVICE_SENSOR_HANDLES_COMPAT = "dsh";
private static final String EXTRA_UID_SENSOR_POSITIONS = "usp";
+ private static final String EXTRA_UID_SENSOR_POSITIONS_COMPAT = "usps";
private final SparseIntArray mSensorPositions = new SparseIntArray();
@@ -47,8 +49,14 @@ public class SensorPowerStatsLayout extends PowerStatsLayout {
super(descriptor);
PersistableBundle extras = descriptor.extras;
- int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
- int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
+ int[] handlers = getIntArray(extras, EXTRA_DEVICE_SENSOR_HANDLES);
+ if (handlers == null) {
+ handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES_COMPAT);
+ }
+ int[] uidDurationPositions = getIntArray(extras, EXTRA_UID_SENSOR_POSITIONS);
+ if (uidDurationPositions == null) {
+ uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS_COMPAT);
+ }
if (handlers != null && uidDurationPositions != null) {
for (int i = 0; i < handlers.length; i++) {
@@ -69,8 +77,8 @@ public class SensorPowerStatsLayout extends PowerStatsLayout {
uidDurationPositions[i] = mSensorPositions.valueAt(i);
}
- extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
- extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
+ putIntArray(extras, EXTRA_DEVICE_SENSOR_HANDLES, handlers);
+ putIntArray(extras, EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
}
private void addUidSensorSection(int handle, String label) {
diff --git a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
index a783d543559f..53894a196d24 100644
--- a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
@@ -25,6 +25,7 @@ import android.os.PersistableBundle;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -347,7 +348,10 @@ class AggregatedPowerStats {
Set<Integer> uids = new HashSet<>();
for (int i = 0; i < mPowerComponentStats.size(); i++) {
- mPowerComponentStats.valueAt(i).collectUids(uids);
+ IntArray activeUids = mPowerComponentStats.valueAt(i).getActiveUids();
+ for (int j = activeUids.size() - 1; j >= 0; j--) {
+ uids.add(activeUids.get(j));
+ }
}
Integer[] allUids = uids.toArray(new Integer[uids.size()]);
diff --git a/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java
index d24ea83540cb..1d359335c6d8 100644
--- a/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/BasePowerStatsProcessor.java
@@ -24,13 +24,12 @@ import static com.android.server.power.stats.processor.AggregatedPowerStatsConfi
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
+import android.util.IntArray;
import com.android.internal.os.PowerStats;
import com.android.server.power.stats.format.BasePowerStatsLayout;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.function.DoubleSupplier;
class BasePowerStatsProcessor extends PowerStatsProcessor {
@@ -125,11 +124,12 @@ class BasePowerStatsProcessor extends PowerStatsProcessor {
mCumulativeDischargeUah = 0;
mCumulativeDischargeDurationMs = 0;
- List<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
-
- long durationMs = timestampMs - mStartTimestamp;
- if (!uids.isEmpty()) {
+ // Note that we are calling `getUids` rather than `getActiveUids`, because this Processor
+ // deals with duration rather than power estimation, so it needs to process *all* known
+ // UIDs, not just the ones that contributed PowerStats
+ IntArray uids = stats.getUids();
+ if (uids.size() != 0) {
+ long durationMs = timestampMs - mStartTimestamp;
for (int i = uids.size() - 1; i >= 0; i--) {
long[] uidStats = new long[sStatsLayout.getUidStatsArrayLength()];
sStatsLayout.setUidUsageDuration(uidStats, durationMs);
diff --git a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
index 9fe7f3e7a542..c89dddf45609 100644
--- a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.os.BatteryStats;
import android.os.PersistableBundle;
import android.os.Process;
+import android.util.IntArray;
import com.android.internal.os.PowerStats;
import com.android.server.power.stats.UsageBasedPowerEstimator;
@@ -27,7 +28,6 @@ import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -190,13 +190,13 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
}
computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported);
- combineDevicePowerEstimates(stats);
- List<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
-
- computeUidActivityTotals(stats, uids);
- computeUidPowerEstimates(stats, uids);
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
+ combineDevicePowerEstimates(stats);
+ computeUidActivityTotals(stats, uids);
+ computeUidPowerEstimates(stats, uids);
+ }
}
protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
@@ -239,8 +239,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
}
}
- private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats,
- List<Integer> uids) {
+ private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, IntArray uids) {
for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
Intermediates intermediates =
@@ -259,8 +258,7 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
}
}
- private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
- List<Integer> uids) {
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, IntArray uids) {
for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
Intermediates intermediates =
@@ -276,12 +274,13 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
int uid = uids.get(k);
if (stats.getUidStats(mTmpUidStatsArray, uid,
proportionalEstimate.stateValues)) {
- double power = intermediates.power
- * mStatsLayout.getUidUsageDuration(mTmpUidStatsArray)
- / intermediates.duration;
- mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
- stats.setUidStats(uid, proportionalEstimate.stateValues,
- mTmpUidStatsArray);
+ long duration = mStatsLayout.getUidUsageDuration(mTmpUidStatsArray);
+ if (duration != 0) {
+ double power = intermediates.power * duration / intermediates.duration;
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues,
+ mTmpUidStatsArray);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
index 4c1a0db02273..c1cd3acf1656 100644
--- a/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
@@ -16,12 +16,13 @@
package com.android.server.power.stats.processor;
+import android.util.IntArray;
+
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
import com.android.server.power.stats.UsageBasedPowerEstimator;
import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
-import java.util.ArrayList;
import java.util.List;
class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
@@ -118,18 +119,19 @@ class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
combineDeviceStateEstimates();
- ArrayList<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
- if (!uids.isEmpty()) {
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(j));
}
}
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(j));
}
}
}
@@ -297,8 +299,10 @@ class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
/ intermediates.txBytes;
}
}
- mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
- stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ if (power != 0) {
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
index 17ceca6e3dc1..ab2489c81449 100644
--- a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
@@ -19,6 +19,7 @@ package com.android.server.power.stats.processor;
import android.annotation.Nullable;
import android.os.BatteryConsumer;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Log;
import com.android.internal.os.CpuScalingPolicies;
@@ -27,7 +28,6 @@ import com.android.internal.os.PowerStats;
import com.android.server.power.stats.format.CpuPowerStatsLayout;
import com.android.server.power.stats.format.WakelockPowerStatsLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -119,6 +119,8 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
mWakelockDescriptor = null;
+
+ initEnergyConsumerToPowerBracketMaps();
}
/**
@@ -157,9 +159,6 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
if (mPlan == null) {
mPlan = new PowerEstimationPlan(stats.getConfig());
- if (mStatsLayout.getEnergyConsumerCount() != 0) {
- initEnergyConsumerToPowerBracketMaps();
- }
}
Intermediates intermediates = new Intermediates();
@@ -189,12 +188,12 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
estimatePowerByDeviceState(stats, intermediates, wakelockStats);
combineDeviceStateEstimates();
- ArrayList<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
- if (!uids.isEmpty()) {
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(i),
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(j),
wakelockStats);
}
}
@@ -255,6 +254,10 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
*/
private void initEnergyConsumerToPowerBracketMaps() {
int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
+ if (energyConsumerCount == 0) {
+ return;
+ }
+
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
@@ -545,8 +548,10 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
power = Math.max(0, power - wakelockPowerEstimate);
}
- mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
- stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ if (power != 0) {
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
index 76adc47cc165..76ea7e841106 100644
--- a/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
@@ -16,10 +16,11 @@
package com.android.server.power.stats.processor;
+import android.util.IntArray;
+
import com.android.internal.os.PowerStats;
import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
-import java.util.ArrayList;
import java.util.List;
class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
@@ -40,10 +41,8 @@ class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
computeDevicePowerEstimates(stats);
- List<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
-
- if (!uids.isEmpty()) {
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
computeUidPowerEstimates(stats, uids);
}
}
@@ -62,7 +61,7 @@ class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
}
private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
- List<Integer> uids) {
+ IntArray uids) {
for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
List<UidStateProportionalEstimate> proportionalEstimates =
@@ -73,9 +72,12 @@ class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
int uid = uids.get(k);
if (stats.getUidStats(mTmpUidStatsArray, uid,
proportionalEstimate.stateValues)) {
- sLayout.setUidPowerEstimate(mTmpUidStatsArray,
- uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0)));
- stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ double power = uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0));
+ if (power != 0) {
+ sLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues,
+ mTmpUidStatsArray);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
index b4c40de862b4..a544daad82f1 100644
--- a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
@@ -19,6 +19,7 @@ import android.os.BatteryStats;
import android.telephony.CellSignalStrength;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -29,7 +30,6 @@ import com.android.internal.power.ModemPowerProfile;
import com.android.server.power.stats.UsageBasedPowerEstimator;
import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -198,18 +198,19 @@ class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
combineDeviceStateEstimates();
- ArrayList<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
- if (!uids.isEmpty()) {
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(j));
}
}
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(j));
}
}
}
@@ -382,8 +383,10 @@ class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
/ intermediates.txPackets;
}
- mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
- stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ if (power != 0) {
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ }
if (DEBUG) {
Slog.d(TAG, "UID: " + uid
diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
index 28474a554b38..69325757c79d 100644
--- a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats.processor;
+import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -333,9 +334,9 @@ class MultiStateStats {
/**
* Adds the delta to the metrics. The number of values must correspond to the dimension count
- * supplied to the Factory constructor
+ * supplied to the Factory constructor. Null values is equivalent to an array of zeros.
*/
- void increment(long[] values, long timestampMs) {
+ void increment(@Nullable long[] values, long timestampMs) {
mCounter.incrementValues(values, timestampMs);
mTracking = true;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
index d4f8fd92fc6c..f9b9da9171cb 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.os.BatteryStats;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -34,7 +35,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
-import java.util.Collection;
import java.util.function.IntConsumer;
/**
@@ -72,11 +72,11 @@ class PowerComponentAggregatedPowerStats {
private MultiStateStats mDeviceStats;
private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
- private long[] mZeroArray;
private static class UidStats {
public int[] states;
public MultiStateStats stats;
+ public boolean hasPowerStats;
public boolean updated;
}
@@ -200,6 +200,7 @@ class PowerComponentAggregatedPowerStats {
if (uidStats.stats == null) {
createUidStats(uidStats, mPowerStatsTimestamp);
}
+ uidStats.hasPowerStats = true;
uidStats.stats.setStats(states, values);
}
@@ -240,6 +241,7 @@ class PowerComponentAggregatedPowerStats {
}
uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
uidStats.updated = true;
+ uidStats.hasPowerStats = true;
}
// For UIDs not mentioned in the PowerStats object, we must assume a 0 increment.
@@ -248,11 +250,8 @@ class PowerComponentAggregatedPowerStats {
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
if (!uidStats.updated && uidStats.stats != null) {
- if (mZeroArray == null
- || mZeroArray.length != mPowerStatsDescriptor.uidStatsArrayLength) {
- mZeroArray = new long[mPowerStatsDescriptor.uidStatsArrayLength];
- }
- uidStats.stats.increment(mZeroArray, timestampMs);
+ // Null stands for an array of zeros
+ uidStats.stats.increment(null, timestampMs);
}
uidStats.updated = false;
}
@@ -267,6 +266,7 @@ class PowerComponentAggregatedPowerStats {
mStateStats.clear();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
mUidStats.valueAt(i).stats = null;
+ mUidStats.valueAt(i).hasPowerStats = false;
}
}
@@ -290,12 +290,26 @@ class PowerComponentAggregatedPowerStats {
return uidStats;
}
- void collectUids(Collection<Integer> uids) {
+ IntArray getUids() {
+ IntArray uids = new IntArray(mUidStats.size());
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ UidStats uidStats = mUidStats.valueAt(i);
+ if (uidStats.stats != null) {
+ uids.add(mUidStats.keyAt(i));
+ }
+ }
+ return uids;
+ }
+
+ IntArray getActiveUids() {
+ IntArray uids = new IntArray(mUidStats.size());
for (int i = mUidStats.size() - 1; i >= 0; i--) {
- if (mUidStats.valueAt(i).stats != null) {
+ UidStats uidStats = mUidStats.valueAt(i);
+ if (uidStats.hasPowerStats) {
uids.add(mUidStats.keyAt(i));
}
}
+ return uids;
}
boolean getDeviceStats(long[] outValues, int[] deviceStates) {
@@ -516,6 +530,7 @@ class PowerComponentAggregatedPowerStats {
if (uidStats.stats == null) {
createUidStats(uidStats, UNKNOWN);
}
+ uidStats.hasPowerStats = true;
if (!uidStats.stats.readFromXml(parser)) {
return false;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
index 177d12988a27..634415ece806 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
@@ -21,6 +21,7 @@ import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.UidBatteryConsumer;
+import android.util.IntArray;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -31,7 +32,6 @@ import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.format.BasePowerStatsLayout;
import com.android.server.power.stats.format.PowerStatsLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -180,8 +180,7 @@ class PowerStatsExporter {
}
}
if (layout.isUidPowerAttributionSupported()) {
- populateBatteryConsumers(batteryUsageStatsBuilder,
- powerComponentStats, layout);
+ populateBatteryConsumers(batteryUsageStatsBuilder, powerComponentStats, layout);
}
populateBatteryLevelInfo(batteryUsageStatsBuilder, batteryLevelInfo);
@@ -258,6 +257,11 @@ class PowerStatsExporter {
BatteryUsageStats.Builder batteryUsageStatsBuilder,
PowerComponentAggregatedPowerStats powerComponentStats,
PowerStatsLayout layout) {
+ IntArray uids = powerComponentStats.getUids();
+ if (uids.size() == 0) {
+ return;
+ }
+
AggregatedPowerStatsConfig.PowerComponent powerComponent = powerComponentStats.getConfig();
PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
long[] uidStats = new long[descriptor.uidStatsArrayLength];
@@ -273,8 +277,6 @@ class PowerStatsExporter {
breakDownByProcState = false;
}
- ArrayList<Integer> uids = new ArrayList<>();
- powerComponentStats.collectUids(uids);
for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
@@ -303,7 +305,7 @@ class PowerStatsExporter {
private void populateUidBatteryConsumers(
BatteryUsageStats.Builder batteryUsageStatsBuilder,
PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout,
- List<Integer> uids, AggregatedPowerStatsConfig.PowerComponent powerComponent,
+ IntArray uids, AggregatedPowerStatsConfig.PowerComponent powerComponent,
long[] uidStats, boolean breakDownByProcState,
@BatteryConsumer.ScreenState int screenState,
@BatteryConsumer.PowerState int powerState) {
@@ -319,7 +321,8 @@ class PowerStatsExporter {
long[] durationByProcState =
new long[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
double powerAllApps = 0;
- for (int uid : uids) {
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
UidBatteryConsumer.Builder builder =
batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
diff --git a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
index 8e7498f38fcb..9df3d7eea27b 100644
--- a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
@@ -27,6 +27,7 @@ import static com.android.server.power.stats.processor.AggregatedPowerStatsConfi
import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
import android.os.BatteryStats;
+import android.util.IntArray;
import android.util.Slog;
import com.android.internal.os.PowerProfile;
@@ -34,7 +35,6 @@ import com.android.internal.os.PowerStats;
import com.android.server.power.stats.UsageBasedPowerEstimator;
import com.android.server.power.stats.format.ScreenPowerStatsLayout;
-import java.util.ArrayList;
import java.util.List;
class ScreenPowerStatsProcessor extends PowerStatsProcessor {
@@ -116,10 +116,8 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor {
computeDevicePowerEstimates(stats);
combineDeviceStateEstimates();
- List<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
-
- if (!uids.isEmpty()) {
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
computeUidPowerEstimates(stats, uids);
}
mPlan.resetIntermediates();
@@ -197,7 +195,7 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor {
}
private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
- List<Integer> uids) {
+ IntArray uids) {
int[] uidStateValues = new int[stats.getConfig().getUidStateConfig().length];
uidStateValues[STATE_SCREEN] = SCREEN_STATE_ON;
uidStateValues[STATE_PROCESS_STATE] = PROCESS_STATE_UNSPECIFIED;
@@ -232,9 +230,11 @@ class ScreenPowerStatsProcessor extends PowerStatsProcessor {
int uid = uids.get(j);
if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
long duration = mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
- double power = intermediates.power * duration / totalTopActivityDuration;
- mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
- stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray);
+ if (duration != 0) {
+ double power = intermediates.power * duration / totalTopActivityDuration;
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
index 0bb028bce5af..ba728d36d561 100644
--- a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
@@ -21,6 +21,7 @@ import android.hardware.SensorManager;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -28,7 +29,6 @@ import com.android.internal.os.PowerStats;
import com.android.server.power.stats.format.PowerStatsLayout;
import com.android.server.power.stats.format.SensorPowerStatsLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
@@ -207,11 +207,11 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor {
mPlan = new PowerEstimationPlan(stats.getConfig());
}
- List<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
-
- computeUidPowerEstimates(stats, uids);
- computeDevicePowerEstimates(stats);
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
+ computeUidPowerEstimates(stats, uids);
+ computeDevicePowerEstimates(stats);
+ }
mPlan.resetIntermediates();
}
@@ -239,9 +239,7 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor {
mLastUpdateTimestamp = timestamp;
}
- private void computeUidPowerEstimates(
- PowerComponentAggregatedPowerStats stats,
- List<Integer> uids) {
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, IntArray uids) {
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
int[] uidSensorDurationPositions = new int[sensorList.size()];
double[] sensorPower = new double[sensorList.size()];
@@ -292,8 +290,7 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor {
}
}
- private void computeDevicePowerEstimates(
- PowerComponentAggregatedPowerStats stats) {
+ private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
CombinedDeviceStateEstimate estimation =
mPlan.combinedDeviceStateEstimations.get(i);
diff --git a/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
index 0df01cf7e5d1..8cc0b6eb6150 100644
--- a/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats.processor;
+import android.util.IntArray;
import android.util.Slog;
import com.android.internal.os.PowerProfile;
@@ -23,7 +24,6 @@ import com.android.internal.os.PowerStats;
import com.android.server.power.stats.UsageBasedPowerEstimator;
import com.android.server.power.stats.format.WifiPowerStatsLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -148,18 +148,19 @@ class WifiPowerStatsProcessor extends PowerStatsProcessor {
combineDeviceStateEstimates();
- ArrayList<Integer> uids = new ArrayList<>();
- stats.collectUids(uids);
- if (!uids.isEmpty()) {
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+ IntArray uids = stats.getActiveUids();
+ if (uids.size() != 0) {
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(j));
}
}
- for (int uid : uids) {
- for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
- computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+ for (int i = uids.size() - 1; i >= 0; i--) {
+ int uid = uids.get(i);
+ for (int j = 0; j < mPlan.uidStateEstimates.size(); j++) {
+ computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(j));
}
}
}
@@ -374,8 +375,10 @@ class WifiPowerStatsProcessor extends PowerStatsProcessor {
/ intermediates.batchedScanDuration;
}
}
- mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
- stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ if (power != 0) {
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ }
if (DEBUG) {
Slog.d(TAG, "UID: " + uid
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
index a75d110e3cd1..17739712d65a 100644
--- a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
+++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
@@ -88,6 +88,5 @@ public class ResourcesManagerShellCommand extends ShellCommand {
out.println(" Print this help text.");
out.println(" dump <PROCESS>");
out.println(" Dump the Resources objects in use as well as the history of Resources");
-
}
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index ab756f2a755b..5347ca46ab31 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -29,7 +29,6 @@ import android.annotation.WorkerThread;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -965,9 +964,7 @@ class Rollback {
ipw.println("-stateDescription: " + mStateDescription);
ipw.println("-timestamp: " + getTimestamp());
ipw.println("-rollbackLifetimeMillis: " + getRollbackLifetimeMillis());
- if (Flags.recoverabilityDetection()) {
- ipw.println("-rollbackImpactLevel: " + info.getRollbackImpactLevel());
- }
+ ipw.println("-rollbackImpactLevel: " + info.getRollbackImpactLevel());
ipw.println("-isStaged: " + isStaged());
ipw.println("-originalSessionId: " + getOriginalSessionId());
ipw.println("-packages:");
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 2e6be5bb56a8..9ed52d8b3504 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1244,17 +1244,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
rollback.makeAvailable();
mPackageHealthObserver.notifyRollbackAvailable(rollback.info);
- if (Flags.recoverabilityDetection()) {
- if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
- // TODO(zezeozue): Provide API to explicitly start observing instead
- // of doing this for all rollbacks. If we do this for all rollbacks,
- // should document in PackageInstaller.SessionParams#setEnableRollback
- // After enabling and committing any rollback, observe packages and
- // prepare to rollback if packages crashes too frequently.
- mPackageWatchdog.startExplicitHealthCheck(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis, mPackageHealthObserver);
- }
- } else {
+ if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
+ // TODO(zezeozue): Provide API to explicitly start observing instead
+ // of doing this for all rollbacks. If we do this for all rollbacks,
+ // should document in PackageInstaller.SessionParams#setEnableRollback
+ // After enabling and committing any rollback, observe packages and
+ // prepare to rollback if packages crashes too frequently.
mPackageWatchdog.startExplicitHealthCheck(rollback.getPackageNames(),
mRollbackLifetimeDurationInMillis, mPackageHealthObserver);
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 50db1e4ac30e..6dc40323f2ee 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -197,9 +197,7 @@ class RollbackStore {
json.put("isStaged", rollback.isStaged());
json.put("causePackages", versionedPackagesToJson(rollback.getCausePackages()));
json.put("committedSessionId", rollback.getCommittedSessionId());
- if (Flags.recoverabilityDetection()) {
- json.put("rollbackImpactLevel", rollback.getRollbackImpactLevel());
- }
+ json.put("rollbackImpactLevel", rollback.getRollbackImpactLevel());
return json;
}
@@ -211,11 +209,9 @@ class RollbackStore {
versionedPackagesFromJson(json.getJSONArray("causePackages")),
json.getInt("committedSessionId"));
- if (Flags.recoverabilityDetection()) {
- // to make it backward compatible.
- rollbackInfo.setRollbackImpactLevel(json.optInt("rollbackImpactLevel",
- PackageManager.ROLLBACK_USER_IMPACT_LOW));
- }
+ // to make it backward compatible.
+ rollbackInfo.setRollbackImpactLevel(json.optInt("rollbackImpactLevel",
+ PackageManager.ROLLBACK_USER_IMPACT_LOW));
return rollbackInfo;
}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
index f060e4d11e82..82df310db9a4 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
@@ -303,7 +303,11 @@ class AttestationVerificationPeerDeviceVerifier {
if (mRevocationEnabled) {
// Checks Revocation Status List based on
// https://developer.android.com/training/articles/security-key-attestation#certificate_status
- mCertificateRevocationStatusManager.checkRevocationStatus(certificates);
+ // The first certificate is the leaf, which is generated at runtime with the attestation
+ // attributes such as the challenge. It is specific to this attestation instance and
+ // does not need to be checked for revocation.
+ mCertificateRevocationStatusManager.checkRevocationStatus(
+ new ArrayList<>(certificates.subList(1, certificates.size())));
}
}
diff --git a/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java b/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
index d36d9f5f6636..4cd4b3b84910 100644
--- a/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
+++ b/services/core/java/com/android/server/security/CertificateRevocationStatusManager.java
@@ -42,6 +42,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.CertPathValidatorException;
import java.security.cert.X509Certificate;
+import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -67,6 +68,8 @@ class CertificateRevocationStatusManager {
*/
@VisibleForTesting static final int MAX_DAYS_SINCE_LAST_CHECK = 30;
+ @VisibleForTesting static final int NUM_HOURS_BEFORE_NEXT_CHECK = 24;
+
/**
* The number of days since issue date for an intermediary certificate to be considered fresh
* and not require a revocation list check.
@@ -127,6 +130,17 @@ class CertificateRevocationStatusManager {
serialNumbers.add(serialNumber);
}
try {
+ if (isLastCheckedWithin(Duration.ofHours(NUM_HOURS_BEFORE_NEXT_CHECK), serialNumbers)) {
+ Slog.d(
+ TAG,
+ "All certificates have been checked for revocation recently. No need to"
+ + " check this time.");
+ return;
+ }
+ } catch (IOException ignored) {
+ // Proceed to check the revocation status
+ }
+ try {
JSONObject revocationList = fetchRemoteRevocationList();
Map<String, Boolean> areCertificatesRevoked = new HashMap<>();
for (String serialNumber : serialNumbers) {
@@ -151,25 +165,32 @@ class CertificateRevocationStatusManager {
serialNumbers.remove(serialNumber);
}
}
- Map<String, LocalDateTime> lastRevocationCheckData;
try {
- lastRevocationCheckData = getLastRevocationCheckData();
+ if (!isLastCheckedWithin(
+ Duration.ofDays(MAX_DAYS_SINCE_LAST_CHECK), serialNumbers)) {
+ throw new CertPathValidatorException(
+ "Unable to verify the revocation status of one of the certificates "
+ + serialNumbers);
+ }
} catch (IOException ex2) {
throw new CertPathValidatorException(
"Unable to load stored revocation status", ex2);
}
- for (String serialNumber : serialNumbers) {
- if (!lastRevocationCheckData.containsKey(serialNumber)
- || lastRevocationCheckData
- .get(serialNumber)
- .isBefore(
- LocalDateTime.now().minusDays(MAX_DAYS_SINCE_LAST_CHECK))) {
- throw new CertPathValidatorException(
- "Unable to verify the revocation status of certificate "
- + serialNumber);
- }
+ }
+ }
+
+ private boolean isLastCheckedWithin(Duration lastCheckedWithin, List<String> serialNumbers)
+ throws IOException {
+ Map<String, LocalDateTime> lastRevocationCheckData = getLastRevocationCheckData();
+ for (String serialNumber : serialNumbers) {
+ if (!lastRevocationCheckData.containsKey(serialNumber)
+ || lastRevocationCheckData
+ .get(serialNumber)
+ .isBefore(LocalDateTime.now().minus(lastCheckedWithin))) {
+ return false;
}
}
+ return true;
}
private static boolean needToCheckRevocationStatus(
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index bfd86d724583..9f9a9807d973 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -54,11 +54,6 @@ public class FileIntegrityService extends SystemService {
super(PermissionEnforcer.fromContext(context));
}
- @Override
- public boolean isApkVeritySupported() {
- return VerityUtils.isFsVeritySupported();
- }
-
private void checkCallerPackageName(String packageName) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
index 687442b47fb3..cdeacaa2e43a 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java
@@ -62,7 +62,7 @@ public class DataAggregator {
/** Initialize DataSources */
private void initialize() {
mDataSources.add(new SecurityLogSource(mContext, this));
- mDataSources.add(new NetworkLogSource(mContext, this));
+ mDataSources.add(new NetworkLogSource(this));
}
/**
diff --git a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
index f303a588d30c..fe0cf80a48f2 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java
@@ -18,7 +18,6 @@ package com.android.server.security.intrusiondetection;
import android.app.admin.ConnectEvent;
import android.app.admin.DnsEvent;
-import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
@@ -44,8 +43,7 @@ public class NetworkLogSource implements DataSource {
private IIpConnectivityMetrics mIpConnectivityMetrics;
private long mId;
- public NetworkLogSource(Context context, DataAggregator dataAggregator)
- throws SecurityException {
+ public NetworkLogSource(DataAggregator dataAggregator) throws SecurityException {
mDataAggregator = dataAggregator;
mPm = LocalServices.getService(PackageManagerInternal.class);
mId = 0;
diff --git a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
index 142094c9d9f4..7501799198e8 100644
--- a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
+++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java
@@ -19,14 +19,15 @@ package com.android.server.security.intrusiondetection;
import android.Manifest.permission;
import android.annotation.RequiresPermission;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.Context;
import android.security.intrusiondetection.IntrusionDetectionEvent;
import android.util.Slog;
+import com.android.server.LocalServices;
+
import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -36,13 +37,13 @@ public class SecurityLogSource implements DataSource {
private SecurityEventCallback mEventCallback;
private DevicePolicyManager mDpm;
- private Executor mExecutor;
+ private DevicePolicyManagerInternal mDpmInternal;
private DataAggregator mDataAggregator;
public SecurityLogSource(Context context, DataAggregator dataAggregator) {
mDataAggregator = dataAggregator;
mDpm = context.getSystemService(DevicePolicyManager.class);
- mExecutor = Executors.newSingleThreadExecutor();
+ mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mEventCallback = new SecurityEventCallback();
}
@@ -50,12 +51,13 @@ public class SecurityLogSource implements DataSource {
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void enable() {
enableAuditLog();
- mDpm.setAuditLogEventCallback(mExecutor, mEventCallback);
+ mDpmInternal.setInternalEventsCallback(mEventCallback);
}
@Override
@RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void disable() {
+ mDpmInternal.setInternalEventsCallback(null);
disableAuditLog();
}
@@ -82,10 +84,11 @@ public class SecurityLogSource implements DataSource {
@Override
public void accept(List<SecurityEvent> events) {
- if (events.size() == 0) {
+ if (events == null || events.size() == 0) {
Slog.w(TAG, "No events received; caller may not be authorized");
return;
}
+
List<IntrusionDetectionEvent> intrusionDetectionEvents =
events.stream()
.filter(event -> event != null)
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 1b6ce9dacfa9..64b52b175252 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -176,14 +176,14 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void onCancel() {
- Slog.d(TAG, "Cancellation signal received, cancelling vibration session...");
+ Slog.d(TAG, "Session cancellation signal received, aborting vibration session...");
requestEndSession(Status.CANCELLED_BY_USER, /* shouldAbort= */ true,
/* isVendorRequest= */ true);
}
@Override
public void binderDied() {
- Slog.d(TAG, "Binder died, cancelling vibration session...");
+ Slog.d(TAG, "Session binder died, aborting vibration session...");
requestEndSession(Status.CANCELLED_BINDER_DIED, /* shouldAbort= */ true,
/* isVendorRequest= */ false);
}
@@ -219,18 +219,20 @@ final class VendorVibrationSession extends IVibrationSession.Stub
@Override
public void notifyVibratorCallback(int vibratorId, long vibrationId) {
- // Ignore it, the session vibration playback doesn't depend on HAL timings
+ Slog.d(TAG, "Vibration callback received for vibration " + vibrationId
+ + " on vibrator " + vibratorId + ", ignoring...");
}
@Override
public void notifySyncedVibratorsCallback(long vibrationId) {
- // Ignore it, the session vibration playback doesn't depend on HAL timings
+ Slog.d(TAG, "Synced vibration callback received for vibration " + vibrationId
+ + ", ignoring...");
}
@Override
public void notifySessionCallback() {
+ Slog.d(TAG, "Session callback received, ending vibration session...");
synchronized (mLock) {
- Slog.d(TAG, "Session callback received, ending vibration session...");
// If end was not requested then the HAL has cancelled the session.
maybeSetEndRequestLocked(Status.CANCELLED_BY_UNKNOWN_REASON,
/* isVendorRequest= */ false);
@@ -307,7 +309,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
}
}
if (isAlreadyEnded) {
- // Session already ended, make sure we end it in the HAL.
+ Slog.d(TAG, "Session already ended after starting the HAL, aborting...");
mHandler.post(() -> mManagerHooks.endSession(mSessionId, /* shouldAbort= */ true));
}
}
@@ -335,8 +337,8 @@ final class VendorVibrationSession extends IVibrationSession.Stub
public boolean maybeSetVibrationConductor(VibrationStepConductor conductor) {
synchronized (mLock) {
if (mConductor != null) {
- Slog.d(TAG, "Vibration session still dispatching previous vibration,"
- + " new vibration ignored");
+ Slog.d(TAG, "Session still dispatching previous vibration, new vibration "
+ + conductor.getVibration().id + " ignored");
return false;
}
mConductor = conductor;
@@ -345,19 +347,22 @@ final class VendorVibrationSession extends IVibrationSession.Stub
}
private void requestEndSession(Status status, boolean shouldAbort, boolean isVendorRequest) {
+ Slog.d(TAG, "Session end request received with status " + status);
boolean shouldTriggerSessionHook = false;
synchronized (mLock) {
maybeSetEndRequestLocked(status, isVendorRequest);
- if (isStarted()) {
- // Always trigger session hook after it has started, in case new request aborts an
- // already finishing session. Wait for HAL callback before actually ending here.
+ if (!isEnded() && isStarted()) {
+ // Trigger session hook even if it was already triggered, in case a second request
+ // is aborting the ongoing/ending session. This might cause it to end right away.
+ // Wait for HAL callback before setting the end status.
shouldTriggerSessionHook = true;
} else {
- // Session did not start in the HAL, end it right away.
+ // Session not active in the HAL, set end status right away.
maybeSetStatusToRequestedLocked();
}
}
if (shouldTriggerSessionHook) {
+ Slog.d(TAG, "Requesting HAL session end with abort=" + shouldAbort);
mHandler.post(() -> mManagerHooks.endSession(mSessionId, shouldAbort));
}
}
@@ -368,6 +373,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
// End already requested, keep first requested status and time.
return;
}
+ Slog.d(TAG, "Session end request accepted for status " + status);
mEndStatusRequest = status;
mEndedByVendor = isVendorRequest;
mEndTime = System.currentTimeMillis();
@@ -400,6 +406,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
// No end status was requested, nothing to set.
return;
}
+ Slog.d(TAG, "Session end request applied for status " + mEndStatusRequest);
mStatus = mEndStatusRequest;
// Run client callback in separate thread.
final Status endStatus = mStatus;
@@ -407,7 +414,7 @@ final class VendorVibrationSession extends IVibrationSession.Stub
try {
mCallback.onFinished(toSessionStatus(endStatus));
} catch (RemoteException e) {
- Slog.e(TAG, "Error notifying vendor session is finishing", e);
+ Slog.e(TAG, "Error notifying vendor session finished", e);
}
});
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 804cf4663bfd..75b1b202bcfd 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -61,6 +61,7 @@ import android.os.VibratorInfo;
import android.os.vibrator.Flags;
import android.os.vibrator.IVibrationSessionCallback;
import android.os.vibrator.PrebakedSegment;
+import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.VibratorInfoFactory;
@@ -786,7 +787,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "Starting session " + session.getSessionId());
+ Slog.d(TAG, "Starting vendor session " + session.getSessionId());
}
Status ignoreStatus = null;
@@ -864,13 +865,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// Session already ended, possibly cancelled by app cancellation signal.
return session.getStatus();
}
- int mode = startAppOpModeLocked(session.getCallerInfo());
+ CallerInfo callerInfo = session.getCallerInfo();
+ int mode = startAppOpModeLocked(callerInfo);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0);
// Make sure mCurrentVibration is set while triggering the HAL.
mCurrentSession = session;
if (!session.linkToDeath()) {
+ // Shouldn't happen. The method call already logs.
+ finishAppOpModeLocked(callerInfo);
mCurrentSession = null;
return Status.IGNORED_ERROR_TOKEN;
}
@@ -878,14 +882,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.e(TAG, "Error starting session " + sessionId + " on vibrators "
+ Arrays.toString(session.getVibratorIds()));
session.unlinkToDeath();
+ finishAppOpModeLocked(callerInfo);
mCurrentSession = null;
return Status.IGNORED_UNSUPPORTED;
}
session.notifyStart();
return null;
case AppOpsManager.MODE_ERRORED:
- Slog.w(TAG, "Start AppOpsManager operation errored for uid "
- + session.getCallerInfo().uid);
+ Slog.w(TAG, "Start AppOpsManager operation errored for uid " + callerInfo.uid);
return Status.IGNORED_ERROR_APP_OPS;
default:
return Status.IGNORED_APP_OPS;
@@ -1081,11 +1085,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Nullable
private Status startVibrationOnThreadLocked(SingleVibrationSession session) {
if (DEBUG) {
- Slog.d(TAG, "Starting vibration " + session.getVibration().id + " on thread");
+ Slog.d(TAG, "Starting vibration " + session.getVibration().id + " on thread");
}
VibrationStepConductor conductor = createVibrationStepConductor(session.getVibration());
session.setVibrationConductor(conductor);
- int mode = startAppOpModeLocked(session.getCallerInfo());
+ CallerInfo callerInfo = session.getCallerInfo();
+ int mode = startAppOpModeLocked(callerInfo);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -1093,19 +1098,21 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mCurrentSession = session;
if (!mCurrentSession.linkToDeath()) {
// Shouldn't happen. The method call already logs.
+ finishAppOpModeLocked(callerInfo);
mCurrentSession = null; // Aborted.
return Status.IGNORED_ERROR_TOKEN;
}
if (!mVibrationThread.runVibrationOnVibrationThread(conductor)) {
// Shouldn't happen. The method call already logs.
session.setVibrationConductor(null); // Rejected by thread, clear it in session.
+ mCurrentSession.unlinkToDeath();
+ finishAppOpModeLocked(callerInfo);
mCurrentSession = null; // Aborted.
return Status.IGNORED_ERROR_SCHEDULING;
}
return null;
case AppOpsManager.MODE_ERRORED:
- Slog.w(TAG, "Start AppOpsManager operation errored for uid "
- + session.getCallerInfo().uid);
+ Slog.w(TAG, "Start AppOpsManager operation errored for uid " + callerInfo.uid);
return Status.IGNORED_ERROR_APP_OPS;
default:
return Status.IGNORED_APP_OPS;
@@ -1494,6 +1501,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private int checkAppOpModeLocked(CallerInfo callerInfo) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg);
+ if (DEBUG) {
+ int opMode = mAppOps.checkOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid,
+ callerInfo.opPkg);
+ Slog.d(TAG, "Check AppOp mode VIBRATE for uid " + callerInfo.uid + " and package "
+ + callerInfo.opPkg + " returned audio=" + AppOpsManager.MODE_NAMES[mode]
+ + ", op=" + AppOpsManager.MODE_NAMES[opMode]);
+ }
int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs);
if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) {
// If we're just ignoring the vibration op then this is set by DND and we should ignore
@@ -1507,9 +1521,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
/** Start an operation in {@link AppOpsManager}, if allowed. */
@GuardedBy("mLock")
private int startAppOpModeLocked(CallerInfo callerInfo) {
- return fixupAppOpModeLocked(
- mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg),
- callerInfo.attrs);
+ int mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid,
+ callerInfo.opPkg);
+ if (DEBUG) {
+ Slog.d(TAG, "Start AppOp mode VIBRATE for uid " + callerInfo.uid + " and package "
+ + callerInfo.opPkg + " returned " + AppOpsManager.MODE_NAMES[mode]);
+ }
+ return fixupAppOpModeLocked(mode, callerInfo.attrs);
}
/**
@@ -1518,6 +1536,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
*/
@GuardedBy("mLock")
private void finishAppOpModeLocked(CallerInfo callerInfo) {
+ if (DEBUG) {
+ Slog.d(TAG, "Finish AppOp mode VIBRATE for uid " + callerInfo.uid + " and package "
+ + callerInfo.opPkg);
+ }
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg);
}
@@ -2651,7 +2673,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
CombinedVibration.ParallelCombination combination =
CombinedVibration.startParallel();
while ("-v".equals(getNextOption())) {
- int vibratorId = Integer.parseInt(getNextArgRequired());
+ int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v");
combination.addVibrator(vibratorId, nextEffect());
}
runVibrate(commonOptions, combination.combine());
@@ -2663,7 +2685,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
CombinedVibration.SequentialCombination combination =
CombinedVibration.startSequential();
while ("-v".equals(getNextOption())) {
- int vibratorId = Integer.parseInt(getNextArgRequired());
+ int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v");
combination.addNext(vibratorId, nextEffect());
}
runVibrate(commonOptions, combination.combine());
@@ -2688,7 +2710,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private int runHapticFeedback() {
CommonOptions commonOptions = new CommonOptions();
- int constant = Integer.parseInt(getNextArgRequired());
+ int constant = parseInt(getNextArgRequired(), "Expected haptic feedback constant id");
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
@@ -2715,6 +2737,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
addPrebakedToComposition(composition);
} else if ("primitives".equals(nextArg)) {
addPrimitivesToComposition(composition);
+ } else if ("envelope".equals(nextArg)) {
+ addEnvelopeToComposition(composition);
} else {
// nextArg is not an effect, finish reading.
break;
@@ -2734,17 +2758,135 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if ("-a".equals(nextOption)) {
hasAmplitude = true;
} else if ("-w".equals(nextOption)) {
- delay = Integer.parseInt(getNextArgRequired());
+ delay = parseInt(getNextArgRequired(), "Expected delay millis after -w");
}
}
- long duration = Long.parseLong(getNextArgRequired());
- int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired())
+ long duration = parseInt(getNextArgRequired(), "Expected one-shot duration millis");
+ int amplitude = hasAmplitude
+ ? parseInt(getNextArgRequired(), "Expected one-shot amplitude")
: VibrationEffect.DEFAULT_AMPLITUDE;
composition.addOffDuration(Duration.ofMillis(delay));
composition.addEffect(VibrationEffect.createOneShot(duration, amplitude));
}
+ private interface EnvelopeBuilder {
+ void setInitialSharpness(float sharpness);
+ void addControlPoint(float intensity, float sharpness, long duration);
+ void reset(float initialSharpness);
+ VibrationEffect build();
+ }
+
+ private static class BasicEnveloperBuilderWrapper implements EnvelopeBuilder {
+ private VibrationEffect.BasicEnvelopeBuilder mBuilder =
+ new VibrationEffect.BasicEnvelopeBuilder();
+
+ @Override
+ public void setInitialSharpness(float sharpness) {
+ mBuilder.setInitialSharpness(sharpness);
+ }
+
+ @Override
+ public void addControlPoint(float intensity, float sharpness, long duration) {
+ mBuilder.addControlPoint(intensity, sharpness, duration);
+ }
+
+ @Override
+ public void reset(float initialSharpness) {
+ mBuilder = new VibrationEffect.BasicEnvelopeBuilder();
+ mBuilder.setInitialSharpness(initialSharpness);
+ }
+
+ @Override
+ public VibrationEffect build() {
+ return mBuilder.build();
+ }
+ }
+
+ private static class AdvancedEnveloperBuilderWrapper implements EnvelopeBuilder {
+ private VibrationEffect.WaveformEnvelopeBuilder mBuilder =
+ new VibrationEffect.WaveformEnvelopeBuilder();
+
+ @Override
+ public void setInitialSharpness(float sharpness) {
+ mBuilder.setInitialFrequencyHz(sharpness);
+ }
+
+ @Override
+ public void addControlPoint(float intensity, float sharpness, long duration) {
+ mBuilder.addControlPoint(intensity, sharpness, duration);
+ }
+
+ @Override
+ public void reset(float initialSharpness) {
+ mBuilder = new VibrationEffect.WaveformEnvelopeBuilder();
+ mBuilder.setInitialFrequencyHz(initialSharpness);
+ }
+
+ @Override
+ public VibrationEffect build() {
+ return mBuilder.build();
+ }
+ }
+
+ private void addEnvelopeToComposition(VibrationEffect.Composition composition) {
+ getNextArgRequired(); // consume "envelope"
+ int repeat = -1;
+ float initialSharpness = Float.NaN;
+ VibrationEffect preamble = null;
+ boolean isAdvanced = false;
+ String nextOption;
+ while ((nextOption = getNextOption()) != null) {
+ switch (nextOption) {
+ case "-a" -> isAdvanced = true;
+ case "-i" -> initialSharpness = parseFloat(getNextArgRequired(),
+ "Expected initial sharpness after -i");
+ case "-r" -> repeat = parseInt(getNextArgRequired(),
+ "Expected repeat index after -r");
+ }
+ }
+
+ EnvelopeBuilder builder = isAdvanced ? new AdvancedEnveloperBuilderWrapper()
+ : new BasicEnveloperBuilderWrapper();
+
+ if (!Float.isNaN(initialSharpness)) {
+ builder.setInitialSharpness(initialSharpness);
+ }
+
+ int duration, pos = 0;
+ float intensity, sharpness = 0f;
+ String nextArg;
+ while ((nextArg = peekNextArg()) != null) {
+ if (pos > 0 && pos == repeat) {
+ preamble = builder.build();
+ builder.reset(sharpness);
+ }
+ try {
+ duration = Integer.parseInt(nextArg);
+ getNextArgRequired(); // consume the duration
+ } catch (NumberFormatException e) {
+ // nextArg is not a duration, finish reading.
+ break;
+ }
+ intensity = parseFloat(getNextArgRequired(), "Expected envelope intensity");
+ sharpness = parseFloat(getNextArgRequired(), "Expected envelope sharpness");
+ builder.addControlPoint(intensity, sharpness, duration);
+ pos++;
+ }
+
+ if (repeat >= 0) {
+ if (preamble == null) {
+ composition.addEffect(VibrationEffect.createRepeatingEffect(builder.build()));
+ } else {
+ composition.addEffect(
+ VibrationEffect.createRepeatingEffect(preamble, builder.build()));
+ }
+ return;
+ }
+
+ composition.addEffect(builder.build());
+ }
+
private void addWaveformToComposition(VibrationEffect.Composition composition) {
boolean hasAmplitudes = false;
boolean hasFrequencies = false;
@@ -2755,16 +2897,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
getNextArgRequired(); // consume "waveform"
String nextOption;
while ((nextOption = getNextOption()) != null) {
- if ("-a".equals(nextOption)) {
- hasAmplitudes = true;
- } else if ("-r".equals(nextOption)) {
- repeat = Integer.parseInt(getNextArgRequired());
- } else if ("-w".equals(nextOption)) {
- delay = Integer.parseInt(getNextArgRequired());
- } else if ("-f".equals(nextOption)) {
- hasFrequencies = true;
- } else if ("-c".equals(nextOption)) {
- isContinuous = true;
+ switch (nextOption) {
+ case "-a" -> hasAmplitudes = true;
+ case "-f" -> hasFrequencies = true;
+ case "-c" -> isContinuous = true;
+ case "-r" -> repeat = parseInt(getNextArgRequired(),
+ "Expected repeat index after -r");
+ case "-w" -> delay = parseInt(getNextArgRequired(),
+ "Expected delay millis after -w");
}
}
List<Integer> durations = new ArrayList<>();
@@ -2782,14 +2922,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
break;
}
if (hasAmplitudes) {
- amplitudes.add(
- Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE);
+ int amplitude = parseInt(getNextArgRequired(), "Expected waveform amplitude");
+ amplitudes.add((float) amplitude / VibrationEffect.MAX_AMPLITUDE);
} else {
amplitudes.add(nextAmplitude);
nextAmplitude = 1 - nextAmplitude;
}
if (hasFrequencies) {
- frequencies.add(Float.parseFloat(getNextArgRequired()));
+ frequencies.add(
+ parseFloat(getNextArgRequired(), "Expected waveform frequency"));
}
}
@@ -2848,27 +2989,37 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if ("-b".equals(nextOption)) {
shouldFallback = true;
} else if ("-w".equals(nextOption)) {
- delay = Integer.parseInt(getNextArgRequired());
+ delay = parseInt(getNextArgRequired(), "Expected delay millis after -w");
}
}
- int effectId = Integer.parseInt(getNextArgRequired());
+ int effectId = parseInt(getNextArgRequired(), "Expected prebaked effect id");
composition.addOffDuration(Duration.ofMillis(delay));
composition.addEffect(VibrationEffect.get(effectId, shouldFallback));
}
private void addPrimitivesToComposition(VibrationEffect.Composition composition) {
getNextArgRequired(); // consume "primitives"
- String nextArg;
- while ((nextArg = peekNextArg()) != null) {
+ while (peekNextArg() != null) {
int delay = 0;
- if ("-w".equals(nextArg)) {
- getNextArgRequired(); // consume "-w"
- delay = Integer.parseInt(getNextArgRequired());
- nextArg = peekNextArg();
+ float scale = 1f;
+ int delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE;
+
+ String nextOption;
+ while ((nextOption = getNextOption()) != null) {
+ if ("-s".equals(nextOption)) {
+ scale = parseFloat(getNextArgRequired(), "Expected scale after -s");
+ } else if ("-o".equals(nextOption)) {
+ delayType = VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET;
+ delay = parseInt(getNextArgRequired(), "Expected offset millis after -o");
+ } else if ("-w".equals(nextOption)) {
+ delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE;
+ delay = parseInt(getNextArgRequired(), "Expected delay millis after -w");
+ }
}
try {
- composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay);
+ String nextArg = peekNextArg(); // Just in case this is not a primitive.
+ composition.addPrimitive(Integer.parseInt(nextArg), scale, delay, delayType);
getNextArgRequired(); // consume the primitive id
} catch (NumberFormatException | NullPointerException e) {
// nextArg is not describing a primitive, leave it to be consumed by outer loops
@@ -2894,17 +3045,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibrationXmlParser.parseDocument(new StringReader(xml));
VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo();
if (combinedVibratorInfo == null) {
- throw new IllegalStateException(
- "No combined vibrator info to parse vibration XML " + xml);
+ throw new IllegalStateException("No vibrator info available to parse XML");
}
VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo);
if (effect == null) {
- throw new IllegalArgumentException(
- "Parsed vibration cannot be resolved for vibration XML " + xml);
+ throw new IllegalArgumentException("Parsed XML cannot be resolved: " + xml);
}
return CombinedVibration.createParallel(effect);
} catch (IOException e) {
- throw new RuntimeException("Error parsing vibration XML " + xml, e);
+ throw new RuntimeException("Error parsing XML: " + xml, e);
}
}
@@ -2922,16 +3071,30 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ private static int parseInt(String text, String errorMessage) {
+ try {
+ return Integer.parseInt(text);
+ } catch (NumberFormatException | NullPointerException e) {
+ throw new IllegalArgumentException(errorMessage, e);
+ }
+ }
+
+ private static float parseFloat(String text, String errorMessage) {
+ try {
+ return Float.parseFloat(text);
+ } catch (NumberFormatException | NullPointerException e) {
+ throw new IllegalArgumentException(errorMessage, e);
+ }
+ }
+
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter();) {
pw.println("Vibrator Manager commands:");
pw.println(" help");
pw.println(" Prints this help text.");
- pw.println("");
pw.println(" list");
- pw.println(" Prints the id of device vibrators. This does not include any ");
- pw.println(" connected input device.");
+ pw.println(" Prints device vibrator ids; does not include input devices.");
pw.println(" synced [options] <effect>...");
pw.println(" Vibrates effect on all vibrators in sync.");
pw.println(" combined [options] (-v <vibrator-id> <effect>...)...");
@@ -2941,58 +3104,63 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println(" xml [options] <xml>");
pw.println(" Vibrates using combined vibration described in given XML string");
pw.println(" on all vibrators in sync. The XML could be:");
- pw.println(" XML containing a single effect, or");
- pw.println(" A vibration select XML containing multiple effects.");
- pw.println(" Vibrates using combined vibration described in given XML string.");
- pw.println(" XML containing a single effect it runs on all vibrators in sync.");
+ pw.println(" A single <vibration-effect>, or");
+ pw.println(" A <vibration-select> containing multiple effects.");
+ pw.println(" feedback [options] <constant>");
+ pw.println(" Performs a haptic feedback with the given constant.");
pw.println(" cancel");
pw.println(" Cancels any active vibration");
- pw.println(" feedback [-f] [-d <description>] <constant>");
- pw.println(" Performs a haptic feedback with the given constant.");
- pw.println(" The force (-f) option enables the `always` configuration, which");
- pw.println(" plays the haptic irrespective of the vibration intensity settings");
pw.println("");
pw.println("Effect commands:");
pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]");
- pw.println(" Vibrates for duration milliseconds; ignored when device is on ");
- pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale amplitude.");
+ pw.println(" Vibrates for duration milliseconds.");
pw.println(" If -w is provided, the effect will be played after the specified");
pw.println(" wait time in milliseconds.");
pw.println(" If -a is provided, the command accepts a second argument for ");
pw.println(" amplitude, in a scale of 1-255.");
pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] ");
pw.println("(<duration> [<amplitude>] [<frequency>])...");
- pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
- pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
- pw.println(" user setting will be used to scale amplitude.");
+ pw.println(" Vibrates for durations and amplitudes in list.");
pw.println(" If -w is provided, the effect will be played after the specified");
pw.println(" wait time in milliseconds.");
pw.println(" If -r is provided, the waveform loops back to the specified");
- pw.println(" index (e.g. 0 loops from the beginning)");
+ pw.println(" index (e.g. 0 loops from the beginning).");
pw.println(" If -a is provided, the command expects amplitude to follow each");
pw.println(" duration; otherwise, it accepts durations only and alternates");
- pw.println(" off/on");
+ pw.println(" off/on.");
pw.println(" If -f is provided, the command expects frequency to follow each");
- pw.println(" amplitude or duration; otherwise, it uses resonant frequency");
+ pw.println(" amplitude or duration; otherwise, it uses resonant frequency.");
pw.println(" If -c is provided, the waveform is continuous and will ramp");
pw.println(" between values; otherwise each entry is a fixed step.");
pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;");
- pw.println(" frequency is an absolute value in hertz;");
+ pw.println(" frequency is an absolute value in hertz.");
+ pw.print(" envelope [-a] [-i initial sharpness] [-r index] ");
+ pw.println("[<duration1> <intensity1> <sharpness1>]...");
+ pw.println(" Generates a vibration pattern based on a series of duration, ");
+ pw.println(" intensity, and sharpness values. The total vibration time is ");
+ pw.println(" the sum of all durations.");
+ pw.println(" If -a is provided, the waveform will use the advanced APIs to ");
+ pw.println(" generate the vibration pattern and the input parameters ");
+ pw.println(" become [<duration1> <amplitude1> <frequency1>].");
+ pw.println(" If -i is provided, the waveform will have an initial sharpness ");
+ pw.println(" it will start from.");
+ pw.println(" If -r is provided, the waveform loops back to the specified index");
+ pw.println(" (e.g. 0 loops from the beginning).");
pw.println(" prebaked [-w delay] [-b] <effect-id>");
- pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
- pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale amplitude.");
+ pw.println(" Vibrates with prebaked effect.");
pw.println(" If -w is provided, the effect will be played after the specified");
pw.println(" wait time in milliseconds.");
pw.println(" If -b is provided, the prebaked fallback effect will be played if");
pw.println(" the device doesn't support the given effect-id.");
- pw.println(" primitives ([-w delay] <primitive-id>)...");
- pw.println(" Vibrates with a composed effect; ignored when device is on DND ");
- pw.println(" (Do Not Disturb) mode; touch feedback strength user setting ");
- pw.println(" will be used to scale primitive intensities.");
+ pw.print(" primitives ([-w delay] [-o time] [-s scale]");
+ pw.println("<primitive-id> [<scale>])...");
+ pw.println(" Vibrates with a composed effect.");
pw.println(" If -w is provided, the next primitive will be played after the ");
pw.println(" specified wait time in milliseconds.");
+ pw.println(" If -o is provided, the next primitive will be played at the ");
+ pw.println(" specified start offset time in milliseconds.");
+ pw.println(" If -s is provided, the next primitive will be played with the");
+ pw.println(" specified amplitude scale, in a scale of [0,1].");
pw.println("");
pw.println("Common Options:");
pw.println(" -f");
@@ -3003,6 +3171,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println(" -d <description>");
pw.println(" Add description to the vibration.");
pw.println("");
+ pw.println("Notes");
+ pw.println(" Vibrations triggered by these commands will be ignored when");
+ pw.println(" device is on DND (Do Not Disturb) mode; notification strength");
+ pw.println(" user settings will be applied for scale.");
+ pw.println("");
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 6cc17d4fa009..a94183849bc5 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -857,8 +857,7 @@ class ActivityMetricsLogger {
info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
info.mIsDrawn = true;
final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
- if (info.mLoggedTransitionStarting || (!r.mDisplayContent.mOpeningApps.contains(r)
- && !r.mTransitionController.isCollecting(r))) {
+ if (info.mLoggedTransitionStarting || !r.mTransitionController.isCollecting(r)) {
done(false /* abort */, info, "notifyWindowsDrawn", timestampNs);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 03fe7775edb0..aa3b500c05f6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -159,7 +159,6 @@ import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_USER_FULLSCREEN_OVERRIDE_ENABLED;
-import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.LAST_DROP_INPUT_MODE;
import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING;
@@ -290,6 +289,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -330,7 +330,6 @@ import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets;
-import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
@@ -354,7 +353,6 @@ import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
-import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.XmlUtils;
@@ -481,6 +479,8 @@ final class ActivityRecord extends WindowToken {
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
+ @Nullable
+ final WindowStyle mWindowStyle;
@VisibleForTesting
int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
@VisibleForTesting
@@ -493,6 +493,7 @@ final class ActivityRecord extends WindowToken {
private long createTime = System.currentTimeMillis();
long lastVisibleTime; // last time this activity became visible
long pauseTime; // last time we started pausing the activity
+ long mStoppedTime; // last time we completely stopped the activity
long launchTickTime; // base time for launch tick messages
long topResumedStateLossTime; // last time we reported top resumed state loss to an activity
// Last configuration reported to the activity in the client process.
@@ -1519,17 +1520,7 @@ final class ActivityRecord extends WindowToken {
this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) {
- if (mTransitionController.isShellTransitionsEnabled()) {
- // For Shell transition, call #initializeChangeTransition directly to take the
- // screenshot at the Activity level. And Shell will be in charge of handling the
- // surface reparent and crop.
- initializeChangeTransition(getBounds());
- } else {
- // For legacy app transition, we want to take a screenshot of the Activity surface,
- // but animate the change transition on TaskFragment level to get the correct window
- // crop.
- newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
- }
+ mTransitionController.collectVisibleChange(this);
}
super.onParentChanged(newParent, oldParent);
@@ -1557,16 +1548,6 @@ final class ActivityRecord extends WindowToken {
mLastReportedPictureInPictureMode = inPinnedWindowingMode();
}
- // When the associated task is {@code null}, the {@link ActivityRecord} can no longer
- // access visual elements like the {@link DisplayContent}. We must remove any associations
- // such as animations.
- if (task == null) {
- // It is possible we have been marked as a closing app earlier. We must remove ourselves
- // from this list so we do not participate in any future animations.
- if (getDisplayContent() != null) {
- getDisplayContent().mClosingApps.remove(this);
- }
- }
final Task rootTask = getRootTask();
if (task == mLastParentBeforePip && task != null) {
// Notify the TaskFragmentOrganizer that the activity is reparented back from pip.
@@ -1749,14 +1730,6 @@ final class ActivityRecord extends WindowToken {
return;
}
prevDc.onRunningActivityChanged();
-
- if (prevDc.mOpeningApps.remove(this)) {
- // Transfer opening transition to new display.
- mDisplayContent.mOpeningApps.add(this);
- mDisplayContent.executeAppTransition();
- }
-
- prevDc.mClosingApps.remove(this);
prevDc.getDisplayPolicy().removeRelaunchingApp(this);
if (prevDc.mFocusedApp == this) {
@@ -1956,22 +1929,17 @@ final class ActivityRecord extends WindowToken {
? android.R.style.Theme : android.R.style.Theme_Holo;
}
- final AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
- realTheme, com.android.internal.R.styleable.Window, mUserId);
-
- if (ent != null) {
- final boolean styleTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean styleFloating = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false);
- mOccludesParent = !(styleTranslucent || styleFloating)
+ final WindowStyle style = mAtmService.getWindowStyle(packageName, realTheme, mUserId);
+ mWindowStyle = style;
+ if (style != null) {
+ mOccludesParent = !(style.isTranslucent() || style.isFloating())
// This style is propagated to the main window attributes with
// FLAG_SHOW_WALLPAPER from PhoneWindow#generateLayout.
- || ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
+ || style.showWallpaper();
mStyleFillsParent = mOccludesParent;
- mNoDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
- mOptOutEdgeToEdge = PhoneWindow.isOptingOutEdgeToEdgeEnforcement(
- aInfo.applicationInfo, false /* local */, ent.array);
+ mNoDisplay = style.noDisplay();
+ mOptOutEdgeToEdge = style.optOutEdgeToEdge() && PhoneWindow.isOptOutEdgeToEdgeEnabled(
+ aInfo.applicationInfo, false /* local */);
} else {
mStyleFillsParent = mOccludesParent = true;
mNoDisplay = false;
@@ -2302,21 +2270,17 @@ final class ActivityRecord extends WindowToken {
return false;
}
- final AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
- com.android.internal.R.styleable.Window, mWmService.mCurrentUserId);
- if (ent == null) {
+ final WindowStyle style = theme == this.theme
+ ? mWindowStyle : mAtmService.getWindowStyle(pkg, theme, mUserId);
+ if (style == null) {
// Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
// see that.
return false;
}
- final boolean windowIsTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowIsFloating = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false);
- final boolean windowShowWallpaper = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false);
- final boolean windowDisableStarting = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false);
+ final boolean windowIsTranslucent = style.isTranslucent();
+ final boolean windowIsFloating = style.isFloating();
+ final boolean windowShowWallpaper = style.showWallpaper();
+ final boolean windowDisableStarting = style.disablePreview();
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
windowIsTranslucent, windowIsFloating, windowShowWallpaper,
@@ -4392,7 +4356,6 @@ final class ActivityRecord extends WindowToken {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);
- getDisplayContent().mOpeningApps.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mSnapshotController.onAppRemoved(this);
mAtmService.mStartingProcessActivities.remove(this);
@@ -4404,20 +4367,9 @@ final class ActivityRecord extends WindowToken {
mAppCompatController.getTransparentPolicy().stop();
// Defer removal of this activity when either a child is animating, or app transition is on
- // going. App transition animation might be applied on the parent task not on the activity,
- // but the actual frame buffer is associated with the activity, so we have to keep the
- // activity while a parent is animating.
- boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION);
- if (getDisplayContent().mClosingApps.contains(this)) {
- delayed = true;
- } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
- getDisplayContent().mClosingApps.add(this);
- delayed = true;
- } else if (mTransitionController.inTransition()) {
- delayed = true;
- }
-
+ // going. The handleCompleteDeferredRemoval will continue the removal.
+ final boolean delayed = isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION)
+ || mTransitionController.inTransition();
// Don't commit visibility if it is waiting to animate. It will be set post animation.
if (!delayed) {
commitVisibility(false /* visible */, true /* performLayout */);
@@ -5552,9 +5504,6 @@ final class ActivityRecord extends WindowToken {
mAtmService.mBackNavigationController.onAppVisibilityChanged(this, visible);
- final DisplayContent displayContent = getDisplayContent();
- displayContent.mOpeningApps.remove(this);
- displayContent.mClosingApps.remove(this);
setVisibleRequested(visible);
mLastDeferHidingClient = deferHidingClient;
@@ -5567,13 +5516,6 @@ final class ActivityRecord extends WindowToken {
setClientVisible(false);
}
} else {
- if (!appTransition.isTransitionSet()
- && appTransition.isReady()) {
- // Add the app mOpeningApps if transition is unset but ready. This means
- // we're doing a screen freeze, and the unfreeze will wait for all opening
- // apps to be ready.
- displayContent.mOpeningApps.add(this);
- }
startingMoved = false;
// If the token is currently hidden (should be the common case), or has been
// stopped, then we need to set up to wait for its windows to be ready.
@@ -6506,6 +6448,7 @@ final class ActivityRecord extends WindowToken {
Slog.w(TAG, "Exception thrown during pause", e);
// Just in case, assume it to be stopped.
mAppStopped = true;
+ mStoppedTime = SystemClock.uptimeMillis();
ProtoLog.v(WM_DEBUG_STATES, "Stop failed; moving to STOPPED: %s", this);
setState(STOPPED, "stopIfPossible");
}
@@ -6539,6 +6482,7 @@ final class ActivityRecord extends WindowToken {
if (isStopping) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to STOPPED: %s (stop complete)", this);
+ mStoppedTime = SystemClock.uptimeMillis();
setState(STOPPED, "activityStopped");
}
@@ -6775,7 +6719,7 @@ final class ActivityRecord extends WindowToken {
setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
// We can now show all of the drawn windows!
- if (!getDisplayContent().mOpeningApps.contains(this) && canShowWindows()) {
+ if (canShowWindows()) {
showAllWindowsLocked();
}
}
@@ -7187,14 +7131,10 @@ final class ActivityRecord extends WindowToken {
if (theme == 0) {
return false;
}
- final AttributeCache.Entry ent = AttributeCache.instance().get(packageName, theme,
- R.styleable.Window, mWmService.mCurrentUserId);
- if (ent != null) {
- if (ent.array.hasValue(R.styleable.Window_windowSplashScreenBehavior)) {
- return ent.array.getInt(R.styleable.Window_windowSplashScreenBehavior,
- SPLASH_SCREEN_BEHAVIOR_DEFAULT)
- == SPLASH_SCREEN_BEHAVIOR_ICON_PREFERRED;
- }
+ final WindowStyle style = theme == this.theme
+ ? mWindowStyle : mAtmService.getWindowStyle(packageName, theme, mUserId);
+ if (style != null) {
+ return style.mSplashScreenBehavior == SPLASH_SCREEN_BEHAVIOR_ICON_PREFERRED;
}
return false;
}
@@ -7449,15 +7389,6 @@ final class ActivityRecord extends WindowToken {
return boundsLayer;
}
- @Override
- boolean isWaitingForTransitionStart() {
- final DisplayContent dc = getDisplayContent();
- return dc != null && dc.mAppTransition.isTransitionSet()
- && (dc.mOpeningApps.contains(this)
- || dc.mClosingApps.contains(this)
- || dc.mChangingContainers.contains(this));
- }
-
boolean isTransitionForward() {
return (mStartingData != null && mStartingData.mIsTransitionForward)
|| mDisplayContent.isNextTransitionForward();
@@ -8051,6 +7982,7 @@ final class ActivityRecord extends WindowToken {
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+ // TODO(b/392069771): Move to AppCompatSandboxingPolicy.
// Sandbox max bounds by setting it to the activity bounds, if activity is letterboxed, or
// has or will have mAppCompatDisplayInsets for size compat. Also forces an activity to be
// sandboxed or not depending upon the configuration settings.
@@ -8079,6 +8011,9 @@ final class ActivityRecord extends WindowToken {
resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
}
+ mAppCompatController.getSandboxingPolicy().sandboxBoundsIfNeeded(resolvedConfig,
+ parentWindowingMode);
+
applySizeOverrideIfNeeded(
mDisplayContent,
info.applicationInfo,
@@ -9557,7 +9492,6 @@ final class ActivityRecord extends WindowToken {
writeNameToProto(proto, NAME);
super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
- proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
proto.write(IS_ANIMATING, isAnimating(TRANSITION | PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION));
proto.write(FILLS_PARENT, fillsParent());
@@ -9713,7 +9647,18 @@ final class ActivityRecord extends WindowToken {
&& !mDisplayContent.isSleeping()) {
// Visibility of starting activities isn't calculated until pause-complete, so if
// this is not paused yet, don't consider it ready.
- return false;
+ // However, due to pip1 having an intermediate state, add a special exception here
+ // that skips waiting if the next activity is already visible.
+ final ActivityRecord toResume = isPip2ExperimentEnabled() ? null
+ : mDisplayContent.getActivity((r) -> !r.finishing
+ && r.isVisibleRequested()
+ && !r.isTaskOverlay()
+ && !r.isAlwaysOnTop());
+ if (toResume == null || !toResume.isVisible()) {
+ return false;
+ } else {
+ Slog.i(TAG, "Assuming sync-finish while pausing due to visible target");
+ }
}
return true;
}
@@ -9872,6 +9817,68 @@ final class ActivityRecord extends WindowToken {
int mBackgroundColor;
}
+ static class WindowStyle {
+ private static final int FLAG_IS_TRANSLUCENT = 1;
+ private static final int FLAG_IS_FLOATING = 1 << 1;
+ private static final int FLAG_SHOW_WALLPAPER = 1 << 2;
+ private static final int FLAG_NO_DISPLAY = 1 << 3;
+ private static final int FLAG_DISABLE_PREVIEW = 1 << 4;
+ private static final int FLAG_OPT_OUT_EDGE_TO_EDGE = 1 << 5;
+
+ final int mFlags;
+
+ @SplashScreenBehavior
+ final int mSplashScreenBehavior;
+
+ WindowStyle(TypedArray array) {
+ int flags = 0;
+ if (array.getBoolean(R.styleable.Window_windowIsTranslucent, false)) {
+ flags |= FLAG_IS_TRANSLUCENT;
+ }
+ if (array.getBoolean(R.styleable.Window_windowIsFloating, false)) {
+ flags |= FLAG_IS_FLOATING;
+ }
+ if (array.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {
+ flags |= FLAG_SHOW_WALLPAPER;
+ }
+ if (array.getBoolean(R.styleable.Window_windowNoDisplay, false)) {
+ flags |= FLAG_NO_DISPLAY;
+ }
+ if (array.getBoolean(R.styleable.Window_windowDisablePreview, false)) {
+ flags |= FLAG_DISABLE_PREVIEW;
+ }
+ if (array.getBoolean(R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false)) {
+ flags |= FLAG_OPT_OUT_EDGE_TO_EDGE;
+ }
+ mFlags = flags;
+ mSplashScreenBehavior = array.getInt(R.styleable.Window_windowSplashScreenBehavior, 0);
+ }
+
+ boolean isTranslucent() {
+ return (mFlags & FLAG_IS_TRANSLUCENT) != 0;
+ }
+
+ boolean isFloating() {
+ return (mFlags & FLAG_IS_FLOATING) != 0;
+ }
+
+ boolean showWallpaper() {
+ return (mFlags & FLAG_SHOW_WALLPAPER) != 0;
+ }
+
+ boolean noDisplay() {
+ return (mFlags & FLAG_NO_DISPLAY) != 0;
+ }
+
+ boolean disablePreview() {
+ return (mFlags & FLAG_DISABLE_PREVIEW) != 0;
+ }
+
+ boolean optOutEdgeToEdge() {
+ return (mFlags & FLAG_OPT_OUT_EDGE_TO_EDGE) != 0;
+ }
+ }
+
static class Builder {
private final ActivityTaskManagerService mAtmService;
private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0a57cb50d681..c243cdc23137 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -617,6 +617,9 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract boolean isBaseOfLockedTask(String packageName);
+ /** Returns the value of {@link android.R.attr#windowNoDisplay} from the given theme. */
+ public abstract boolean isNoDisplay(String packageName, int theme, int userId);
+
/**
* Creates an interface to update configuration for the calling application.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 819e117e6d05..6f83822ee97a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -286,6 +286,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
+import com.android.server.wm.utils.WindowStyleCache;
import com.android.wm.shell.Flags;
import java.io.BufferedReader;
@@ -500,6 +501,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mSuppressResizeConfigChanges;
+ private final WindowStyleCache<ActivityRecord.WindowStyle> mWindowStyleCache =
+ new WindowStyleCache<>(ActivityRecord.WindowStyle::new);
final UpdateConfigurationResult mTmpUpdateConfigurationResult =
new UpdateConfigurationResult();
@@ -2127,6 +2130,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public boolean setTaskIsPerceptible(int taskId, boolean isPerceptible) {
+ enforceTaskPermission("setTaskIsPerceptible()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_ATTACHED_TASK_ONLY);
+ if (task == null) {
+ Slog.w(TAG, "setTaskIsPerceptible: No task to set with id=" + taskId);
+ return false;
+ }
+ task.mIsPerceptible = isPerceptible;
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
@@ -5570,6 +5593,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mUserManagerInternal;
}
+ @Nullable
+ ActivityRecord.WindowStyle getWindowStyle(String packageName, int theme, int userId) {
+ if (!com.android.window.flags.Flags.cacheWindowStyle()) {
+ final AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
+ theme, com.android.internal.R.styleable.Window, userId);
+ return ent != null ? new ActivityRecord.WindowStyle(ent.array) : null;
+ }
+ return mWindowStyleCache.get(packageName, theme, userId);
+ }
+
AppWarnings getAppWarningsLocked() {
return mAppWarnings;
}
@@ -6518,6 +6551,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mCompatModePackages.handlePackageUninstalledLocked(name);
mPackageConfigPersister.onPackageUninstall(name, userId);
}
+ mWindowStyleCache.invalidatePackage(name);
}
@Override
@@ -6534,6 +6568,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (mRootWindowContainer == null) return;
mRootWindowContainer.updateActivityApplicationInfo(aInfo);
}
+ mWindowStyleCache.invalidatePackage(aInfo.packageName);
}
@Override
@@ -7433,6 +7468,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public boolean isNoDisplay(String packageName, int theme, int userId) {
+ if (!com.android.window.flags.Flags.cacheWindowStyle()) {
+ final AttributeCache.Entry ent = AttributeCache.instance()
+ .get(packageName, theme, R.styleable.Window, userId);
+ return ent != null
+ && ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ }
+ final ActivityRecord.WindowStyle style = getWindowStyle(packageName, theme, userId);
+ return style != null && style.noDisplay();
+ }
+
+ @Override
public PackageConfigurationUpdater createPackageConfigurationUpdater() {
return new PackageConfigurationUpdaterImpl(Binder.getCallingPid(),
ActivityTaskManagerService.this);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 6a5adca91e39..463a92fb55bb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -145,6 +145,7 @@ import android.util.SparseIntArray;
import android.view.Display;
import android.webkit.URLUtil;
import android.window.ActivityWindowInfo;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import com.android.internal.R;
@@ -899,108 +900,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
}
- try {
- if (!proc.hasThread()) {
- throw new RemoteException();
- }
- List<ResultInfo> results = null;
- List<ReferrerIntent> newIntents = null;
- if (andResume) {
- // We don't need to deliver new intents and/or set results if activity is going
- // to pause immediately after launch.
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Launching: " + r + " savedState=" + r.getSavedState()
- + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
- EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r),
- task.mTaskId, r.shortComponentName);
- updateHomeProcessIfNeeded(r);
- mService.getPackageManagerInternalLocked().notifyPackageUse(
- r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
- mService.getAppWarningsLocked().onStartActivity(r);
-
- final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
- final Configuration overrideConfig = r.getMergedOverrideConfiguration();
- r.setLastReportedConfiguration(procConfig, overrideConfig);
-
- final ActivityWindowInfo activityWindowInfo = r.getActivityWindowInfo();
- r.setLastReportedActivityWindowInfo(activityWindowInfo);
-
- logIfTransactionTooLarge(r.intent, r.getSavedState());
-
- final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
- if (organizedTaskFragment != null) {
- // Sending TaskFragmentInfo to client to ensure the info is updated before
- // the activity creation.
- mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
- organizedTaskFragment);
- }
-
- // Create activity launch transaction.
- final boolean isTransitionForward = r.isTransitionForward();
- final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
- final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
- final LaunchActivityItem launchActivityItem = new LaunchActivityItem(r.token,
- r.intent, System.identityHashCode(r), r.info,
- procConfig, overrideConfig, deviceId,
- r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
- proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
- results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward,
- proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
- r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken,
- r.initialCallerInfoAccessToken, activityWindowInfo);
-
- // Set desired final state.
- final ActivityLifecycleItem lifecycleItem;
- if (andResume) {
- lifecycleItem = new ResumeActivityItem(r.token, isTransitionForward,
- r.shouldSendCompatFakeFocus());
- } else if (r.isVisibleRequested()) {
- lifecycleItem = new PauseActivityItem(r.token);
- } else {
- lifecycleItem = new StopActivityItem(r.token);
- }
-
- // Schedule transaction.
- if (shouldDispatchLaunchActivityItemIndependently(r.info.packageName, r.getUid())) {
- // LaunchActivityItem has @UnsupportedAppUsage usages.
- // Guard with targetSDK on Android 15+.
- // To not bundle the transaction, dispatch the pending before schedule new
- // transaction.
- mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread());
- }
- mService.getLifecycleManager().scheduleTransactionItems(
- proc.getThread(),
- // Immediately dispatch the transaction, so that if it fails, the server can
- // restart the process and retry now.
- true /* shouldDispatchImmediately */,
- launchActivityItem, lifecycleItem);
-
- if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) {
- // If the seq is increased, there should be something changed (e.g. registered
- // activity configuration).
- proc.setLastReportedConfiguration(procConfig);
- }
- if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
- && mService.mHasHeavyWeightFeature) {
- // This may be a heavy-weight process! Note that the package manager will ensure
- // that only activity can run in the main process of the .apk, which is the only
- // thing that will be considered heavy-weight.
- if (proc.mName.equals(proc.mInfo.packageName)) {
- if (mService.mHeavyWeightProcess != null
- && mService.mHeavyWeightProcess != proc) {
- Slog.w(TAG, "Starting new heavy weight process " + proc
- + " when already running "
- + mService.mHeavyWeightProcess);
- }
- mService.setHeavyWeightProcess(r);
- }
- }
-
- } catch (RemoteException e) {
+ final RemoteException e = tryRealStartActivityInner(
+ task, r, proc, activityClientController, andResume);
+ if (e != null) {
if (r.launchFailed) {
// This is the second time we failed -- finish activity and give up.
Slog.e(TAG, "Second failure launching "
@@ -1023,7 +925,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
r.launchFailed = false;
- // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
+ // Resume or pause requests are done as part of launch transaction,
// so updating the state should be done accordingly.
if (andResume && readyToResume()) {
// As part of the process of launching, ActivityThread also performs
@@ -1063,6 +965,122 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return true;
}
+ /** @return {@link RemoteException} if the app process failed to handle the activity start. */
+ @Nullable
+ private RemoteException tryRealStartActivityInner(
+ @NonNull Task task,
+ @NonNull ActivityRecord r,
+ @NonNull WindowProcessController proc,
+ @Nullable IActivityClientController activityClientController,
+ boolean andResume) {
+ if (!proc.hasThread()) {
+ return new RemoteException();
+ }
+ List<ResultInfo> results = null;
+ List<ReferrerIntent> newIntents = null;
+ if (andResume) {
+ // We don't need to deliver new intents and/or set results if activity is going
+ // to pause immediately after launch.
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH,
+ "Launching: " + r + " savedState=" + r.getSavedState()
+ + " with results=" + results + " newIntents=" + newIntents
+ + " andResume=" + andResume);
+ }
+ EventLogTags.writeWmRestartActivity(r.mUserId, System.identityHashCode(r),
+ task.mTaskId, r.shortComponentName);
+ updateHomeProcessIfNeeded(r);
+ mService.getPackageManagerInternalLocked().notifyPackageUse(
+ r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
+ mService.getAppWarningsLocked().onStartActivity(r);
+
+ final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
+ final Configuration overrideConfig = r.getMergedOverrideConfiguration();
+ r.setLastReportedConfiguration(procConfig, overrideConfig);
+
+ final ActivityWindowInfo activityWindowInfo = r.getActivityWindowInfo();
+ r.setLastReportedActivityWindowInfo(activityWindowInfo);
+
+ logIfTransactionTooLarge(r.intent, r.getSavedState());
+
+ final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
+ if (organizedTaskFragment != null) {
+ // Sending TaskFragmentInfo to client to ensure the info is updated before
+ // the activity creation.
+ mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
+ organizedTaskFragment);
+ }
+
+ // Create activity launch transaction.
+ final boolean isTransitionForward = r.isTransitionForward();
+ final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
+ final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
+ final LaunchActivityItem launchActivityItem = new LaunchActivityItem(r.token,
+ r.intent, System.identityHashCode(r), r.info,
+ procConfig, overrideConfig, deviceId,
+ r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
+ proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
+ results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward,
+ proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
+ r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken,
+ r.initialCallerInfoAccessToken, activityWindowInfo);
+
+ // Set desired final state.
+ final ActivityLifecycleItem lifecycleItem;
+ if (andResume) {
+ lifecycleItem = new ResumeActivityItem(r.token, isTransitionForward,
+ r.shouldSendCompatFakeFocus());
+ } else if (r.isVisibleRequested()) {
+ lifecycleItem = new PauseActivityItem(r.token);
+ } else {
+ lifecycleItem = new StopActivityItem(r.token);
+ }
+
+ // Schedule transaction.
+ if (shouldDispatchLaunchActivityItemIndependently(r.info.packageName, r.getUid())) {
+ // LaunchActivityItem has @UnsupportedAppUsage usages.
+ // Guard with targetSDK on Android 15+.
+ // To not bundle the transaction, dispatch the pending before schedule new
+ // transaction.
+ mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread());
+ }
+ try {
+ mService.getLifecycleManager().scheduleTransactionItems(
+ proc.getThread(),
+ // Immediately dispatch the transaction, so that if it fails, the server can
+ // restart the process and retry now.
+ true /* shouldDispatchImmediately */,
+ launchActivityItem, lifecycleItem);
+ } catch (RemoteException e) {
+ return e;
+ }
+
+ if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) {
+ // If the seq is increased, there should be something changed (e.g. registered
+ // activity configuration).
+ proc.setLastReportedConfiguration(procConfig);
+ }
+ if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
+ && mService.mHasHeavyWeightFeature) {
+ // This may be a heavy-weight process! Note that the package manager will ensure
+ // that only activity can run in the main process of the .apk, which is the only
+ // thing that will be considered heavy-weight.
+ if (proc.mName.equals(proc.mInfo.packageName)) {
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != proc) {
+ Slog.w(TAG, "Starting new heavy weight process " + proc
+ + " when already running "
+ + mService.mHeavyWeightProcess);
+ }
+ mService.setHeavyWeightProcess(r);
+ }
+ }
+ return null;
+ }
+
void updateHomeProcessIfNeeded(@NonNull ActivityRecord r) {
if (!r.isActivityTypeHome()) return;
// Make sure that we use the bottom most activity from the same package, because the home
@@ -2916,6 +2934,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
/** The helper to calculate whether a container is opaque. */
static class OpaqueContainerHelper implements Predicate<ActivityRecord> {
+ private final boolean mEnableMultipleDesktopsBackend =
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue();
private ActivityRecord mStarting;
private boolean mIgnoringInvisibleActivity;
private boolean mIgnoringKeyguard;
@@ -2938,7 +2958,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
mIgnoringKeyguard = ignoringKeyguard;
final boolean isOpaque;
- if (!Flags.enableMultipleDesktopsBackend()) {
+ if (!mEnableMultipleDesktopsBackend) {
isOpaque = container.getActivity(this,
true /* traverseTopToBottom */, null /* boundary */) != null;
} else {
@@ -2949,13 +2969,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
private boolean isOpaqueInner(@NonNull WindowContainer<?> container) {
- // If it's a leaf task fragment, then opacity is calculated based on its activities.
- if (container.asTaskFragment() != null
- && ((TaskFragment) container).isLeafTaskFragment()) {
+ final boolean isActivity = container.asActivityRecord() != null;
+ final boolean isLeafTaskFragment = container.asTaskFragment() != null
+ && ((TaskFragment) container).isLeafTaskFragment();
+ if (isActivity || isLeafTaskFragment) {
+ // When it is an activity or leaf task fragment, then opacity is calculated based
+ // on itself or its activities.
return container.getActivity(this,
true /* traverseTopToBottom */, null /* boundary */) != null;
}
- // When not a leaf, it's considered opaque if any of its opaque children fill this
+ // Otherwise, it's considered opaque if any of its opaque children fill this
// container, unless the children are adjacent fragments, in which case as long as they
// are all opaque then |container| is also considered opaque, even if the adjacent
// task fragment aren't filling.
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index bed95face1c9..fc504796b0ac 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -44,6 +44,8 @@ class AppCompatController {
private final AppCompatLetterboxPolicy mLetterboxPolicy;
@NonNull
private final AppCompatSizeCompatModePolicy mSizeCompatModePolicy;
+ @NonNull
+ private final AppCompatSandboxingPolicy mSandboxingPolicy;
AppCompatController(@NonNull WindowManagerService wmService,
@NonNull ActivityRecord activityRecord) {
@@ -66,6 +68,7 @@ class AppCompatController {
mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
mSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
mAppCompatOverrides);
+ mSandboxingPolicy = new AppCompatSandboxingPolicy(activityRecord);
}
@NonNull
@@ -143,6 +146,11 @@ class AppCompatController {
return mSizeCompatModePolicy;
}
+ @NonNull
+ AppCompatSandboxingPolicy getSandboxingPolicy() {
+ return mSandboxingPolicy;
+ }
+
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
getTransparentPolicy().dump(pw, prefix);
getLetterboxPolicy().dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
index dff072e2dcf8..57811e24351f 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -16,12 +16,14 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.window.DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
@@ -48,6 +50,8 @@ import java.io.PrintWriter;
*/
class AppCompatLetterboxPolicy {
+ private static final int DIFF_TOLERANCE_PX = 1;
+
@NonNull
private final ActivityRecord mActivityRecord;
@NonNull
@@ -56,6 +60,9 @@ class AppCompatLetterboxPolicy {
private final AppCompatRoundedCorners mAppCompatRoundedCorners;
@NonNull
private final AppCompatConfiguration mAppCompatConfiguration;
+ // Convenience temporary object to save allocation when calculating Rect.
+ @NonNull
+ private final Rect mTmpRect = new Rect();
private boolean mLastShouldShowLetterboxUi;
@@ -71,7 +78,7 @@ class AppCompatLetterboxPolicy {
: new LegacyLetterboxPolicyState();
// TODO (b/358334569) Improve cutout logic dependency on app compat.
mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord,
- this::isLetterboxedNotForDisplayCutout);
+ this::ieEligibleForRoundedCorners);
mAppCompatConfiguration = appCompatConfiguration;
}
@@ -84,7 +91,7 @@ class AppCompatLetterboxPolicy {
mLetterboxPolicyState.stop();
}
- /** @return {@value true} if the letterbox policy is running and the activity letterboxed. */
+ /** @return {@code true} if the letterbox policy is running and the activity letterboxed. */
boolean isRunning() {
return mLetterboxPolicyState.isRunning();
}
@@ -130,7 +137,7 @@ class AppCompatLetterboxPolicy {
* <li>The activity is in fullscreen.
* <li>The activity is portrait-only.
* <li>The activity doesn't have a starting window (education should only be displayed
- * once the starting window is removed in {@link #removeStartingWindow}).
+ * once the starting window is removed in {@link ActivityRecord#removeStartingWindow}).
* </ul>
*/
boolean isEligibleForLetterboxEducation() {
@@ -294,16 +301,40 @@ class AppCompatLetterboxPolicy {
}
}
+ private boolean ieEligibleForRoundedCorners(@NonNull WindowState mainWindow) {
+ return isLetterboxedNotForDisplayCutout(mainWindow)
+ && !isFreeformActivityMatchParentAppBoundsHeight();
+ }
+
private boolean isLetterboxedNotForDisplayCutout(@NonNull WindowState mainWindow) {
return shouldShowLetterboxUi(mainWindow)
&& !mainWindow.isLetterboxedForDisplayCutout();
}
+ private boolean isFreeformActivityMatchParentAppBoundsHeight() {
+ if (!EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue()) {
+ return false;
+ }
+ final Task task = mActivityRecord.getTask();
+ if (task == null) {
+ return false;
+ }
+ final Rect parentAppBounds = task.getWindowConfiguration().getAppBounds();
+ if (parentAppBounds == null) {
+ return false;
+ }
+
+ mLetterboxPolicyState.getLetterboxInnerBounds(mTmpRect);
+ final int diff = parentAppBounds.height() - mTmpRect.height();
+ // Compare bounds with tolerance of 1 px to account for rounding error calculations.
+ return task.getWindowingMode() == WINDOWING_MODE_FREEFORM && diff <= DIFF_TOLERANCE_PX;
+ }
+
private static boolean shouldNotLayoutLetterbox(@Nullable WindowState w) {
if (w == null) {
return true;
}
- final int type = w.mAttrs.type;
+ final int type = w.getAttrs().type;
// Allow letterbox to be displayed early for base application or application starting
// windows even if it is not on the top z order to prevent flickering when the
// letterboxed window is brought to the top
diff --git a/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
index 92d76e5616d8..8165638d4bda 100644
--- a/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
+++ b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
@@ -35,12 +35,12 @@ class AppCompatRoundedCorners {
@NonNull
private final ActivityRecord mActivityRecord;
@NonNull
- private final Predicate<WindowState> mIsLetterboxedNotForDisplayCutout;
+ private final Predicate<WindowState> mRoundedCornersWindowCondition;
AppCompatRoundedCorners(@NonNull ActivityRecord activityRecord,
- @NonNull Predicate<WindowState> isLetterboxedNotForDisplayCutout) {
+ @NonNull Predicate<WindowState> roundedCornersWindowCondition) {
mActivityRecord = activityRecord;
- mIsLetterboxedNotForDisplayCutout = isLetterboxedNotForDisplayCutout;
+ mRoundedCornersWindowCondition = roundedCornersWindowCondition;
}
void updateRoundedCornersIfNeeded(@NonNull final WindowState mainWindow) {
@@ -136,7 +136,7 @@ class AppCompatRoundedCorners {
private boolean requiresRoundedCorners(@NonNull final WindowState mainWindow) {
final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
.mAppCompatController.getLetterboxOverrides();
- return mIsLetterboxedNotForDisplayCutout.test(mainWindow)
+ return mRoundedCornersWindowCondition.test(mainWindow)
&& letterboxOverrides.isLetterboxActivityCornersRounded();
}
diff --git a/services/core/java/com/android/server/wm/AppCompatSandboxingPolicy.java b/services/core/java/com/android/server/wm/AppCompatSandboxingPolicy.java
new file mode 100644
index 000000000000..6a46f57e2640
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatSandboxingPolicy.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.window.DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS;
+
+import static com.android.server.wm.AppCompatUtils.isInDesktopMode;
+
+import android.annotation.NonNull;
+import android.app.WindowConfiguration.WindowingMode;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+/**
+ * Encapsulate logic related to sandboxing for app compatibility.
+ */
+class AppCompatSandboxingPolicy {
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+
+ AppCompatSandboxingPolicy(@NonNull ActivityRecord activityRecord) {
+ mActivityRecord = activityRecord;
+ }
+
+ /**
+ * In freeform, the container bounds are scaled with app bounds. Activity bounds can be
+ * outside of its container bounds if insets are coupled with configuration outside of
+ * freeform and maintained in freeform for size compat mode.
+ *
+ * <p>Sandbox activity bounds in freeform to app bounds to force app to display within the
+ * container. This prevents UI cropping when activities can draw below insets which are
+ * normally excluded from appBounds before targetSDK < 35
+ * (see ConfigurationContainer#applySizeOverrideIfNeeded).
+ */
+ void sandboxBoundsIfNeeded(@NonNull Configuration resolvedConfig,
+ @WindowingMode int windowingMode) {
+ if (!EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue()) {
+ return;
+ }
+
+ if (isInDesktopMode(mActivityRecord.mAtmService.mContext, windowingMode)) {
+ Rect appBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ if (appBounds == null || appBounds.isEmpty()) {
+ // When there is no override bounds, the activity will inherit the bounds from
+ // parent.
+ appBounds = mActivityRecord.mResolveConfigHint.mParentAppBoundsOverride;
+ }
+ resolvedConfig.windowConfiguration.setBounds(appBounds);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index bbc33004ee54..2cfa242bc5fe 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -17,14 +17,13 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_METADATA;
import static android.content.pm.ActivityInfo.SIZE_CHANGES_SUPPORTED_OVERRIDE;
import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_METADATA;
import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
+import static com.android.server.wm.AppCompatUtils.isInDesktopMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -545,9 +544,8 @@ class AppCompatSizeCompatModePolicy {
// Allow an application to be up-scaled if its window is smaller than its
// original container or if it's a freeform window in desktop mode.
boolean shouldAllowUpscaling = !(contentW <= viewportW && contentH <= viewportH)
- || (canEnterDesktopMode(mActivityRecord.mAtmService.mContext)
- && newParentConfig.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_FREEFORM);
+ || isInDesktopMode(mActivityRecord.mAtmService.mContext,
+ newParentConfig.windowConfiguration.getWindowingMode());
return shouldAllowUpscaling ? Math.min(
(float) viewportW / contentW, (float) viewportH / contentH) : 1f;
}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 3e054fc40540..146044008b3f 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -16,16 +16,20 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppCompatTaskInfo;
import android.app.CameraCompatTaskInfo;
import android.app.TaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.InsetsSource;
@@ -276,6 +280,14 @@ final class AppCompatUtils {
inOutConfig.windowConfiguration.getAppBounds().offset(offsetX, offsetY);
}
+ /**
+ * Return {@code true} if window is currently in desktop mode.
+ */
+ static boolean isInDesktopMode(@NonNull Context context,
+ @WindowingMode int parentWindowingMode) {
+ return parentWindowingMode == WINDOWING_MODE_FREEFORM && canEnterDesktopMode(context);
+ }
+
private static void clearAppCompatTaskInfo(@NonNull AppCompatTaskInfo info) {
info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d98ad8bb9e05..12d4a210400c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -89,7 +89,6 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
@@ -1554,26 +1553,6 @@ public class AppTransition implements Dump {
}
private void handleAppTransitionTimeout() {
- synchronized (mService.mGlobalLock) {
- final DisplayContent dc = mDisplayContent;
- if (dc == null) {
- return;
- }
- notifyAppTransitionTimeoutLocked();
- if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
- || !dc.mChangingContainers.isEmpty()) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
- + "mOpeningApps.size()=%d mClosingApps.size()=%d "
- + "mChangingApps.size()=%d",
- dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
- dc.mOpeningApps.size(), dc.mClosingApps.size(),
- dc.mChangingContainers.size());
-
- setTimeout();
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
}
private static void doAnimationCallback(@NonNull IRemoteCallback callback) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index e76a83453a9d..d652ea1e26a4 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -190,7 +190,9 @@ class BackNavigationController {
currentActivity = window.mActivityRecord;
currentTask = window.getTask();
if ((currentTask != null && !currentTask.isVisibleRequested())
- || (currentActivity != null && !currentActivity.isVisibleRequested())) {
+ || (currentActivity != null && !currentActivity.isVisibleRequested())
+ || (currentActivity != null && currentTask != null
+ && currentTask.getTopNonFinishingActivity() != currentActivity)) {
// Closing transition is happening on focus window and should be update soon,
// don't drive back navigation with it.
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Focus window is closing.");
@@ -279,6 +281,10 @@ class BackNavigationController {
} else if (hasTranslucentActivity(currentActivity, prevActivities)) {
// skip if one of participant activity is translucent
backType = BackNavigationInfo.TYPE_CALLBACK;
+ } else if (!allActivitiesHaveProcesses(prevActivities)) {
+ // Skip if one of previous activity has no process. Restart process can be slow, and
+ // the final hierarchy could be different.
+ backType = BackNavigationInfo.TYPE_CALLBACK;
} else if (prevActivities.size() > 0
&& requestOverride == SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED) {
if ((!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities))
@@ -601,6 +607,17 @@ class BackNavigationController {
return false;
}
+ private static boolean allActivitiesHaveProcesses(
+ @NonNull ArrayList<ActivityRecord> prevActivities) {
+ for (int i = prevActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord test = prevActivities.get(i);
+ if (!test.hasProcess()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private static boolean isAllActivitiesCanShowWhenLocked(
@NonNull ArrayList<ActivityRecord> prevActivities) {
for (int i = prevActivities.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 05dcbb7f9af4..aaa18ad6acc9 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -257,10 +257,12 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
// This should be the only place override the configuration for ActivityRecord. Override
// the value if not calculated yet.
Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ Rect outConfigBounds = new Rect(outAppBounds);
if (outAppBounds == null || outAppBounds.isEmpty()) {
inOutConfig.windowConfiguration.setAppBounds(
newParentConfiguration.windowConfiguration.getBounds());
outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ outConfigBounds.set(outAppBounds);
if (task != null) {
task = task.getCreatedByOrganizerTask();
if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
@@ -279,6 +281,12 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
outAppBounds.inset(decor.mOverrideNonDecorInsets);
}
}
+ if (!outConfigBounds.intersect(decor.mOverrideConfigFrame)) {
+ if (inOutConfig.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_MULTI_WINDOW) {
+ outAppBounds.inset(decor.mOverrideConfigInsets);
+ }
+ }
if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
outAppBounds.offset(-task.mOffsetXForInsets, -task.mOffsetYForInsets);
}
@@ -289,10 +297,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
+ inOutConfig.screenWidthDp = (int) (outConfigBounds.width() / density + 0.5f);
}
if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
+ inOutConfig.screenHeightDp = (int) (outConfigBounds.height() / density + 0.5f);
}
if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
&& parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 4eaa11bac016..f473b7b7e4fb 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -60,10 +60,11 @@ class DeferredDisplayUpdater {
*/
@VisibleForTesting
static final DisplayInfoFieldsUpdater DEFERRABLE_FIELDS = (out, override) -> {
- // Treat unique id and address change as WM-specific display change as we re-query display
- // settings and parameters based on it which could cause window changes
+ // Treat unique id, address, and canHostTasks change as WM-specific display change as we
+ // re-query display settings and parameters based on it which could cause window changes.
out.uniqueId = override.uniqueId;
out.address = override.address;
+ out.canHostTasks = override.canHostTasks;
// Also apply WM-override fields, since they might produce differences in window hierarchy
WM_OVERRIDE_FIELDS.setFields(out, override);
@@ -433,7 +434,7 @@ class DeferredDisplayUpdater {
second.thermalRefreshRateThrottling)
|| !Objects.equals(first.thermalBrightnessThrottlingDataId,
second.thermalBrightnessThrottlingDataId)
- || first.canHostTasks != second.canHostTasks) {
+ ) {
diff |= DIFF_NOT_WM_DEFERRABLE;
}
@@ -454,6 +455,7 @@ class DeferredDisplayUpdater {
|| !Objects.equals(first.displayShape, second.displayShape)
|| !Objects.equals(first.uniqueId, second.uniqueId)
|| !Objects.equals(first.address, second.address)
+ || first.canHostTasks != second.canHostTasks
) {
diff |= DIFF_WM_DEFERRABLE;
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index f35930700653..c2255d8d011a 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -51,13 +51,8 @@ public final class DesktopModeHelper {
}
/**
- * Return {@code true} if the current device can hosts desktop sessions on its internal display.
+ * Return {@code true} if the current device supports desktop mode.
*/
- @VisibleForTesting
- static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
- return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops);
- }
-
// TODO(b/337819319): use a companion object instead.
private static boolean isDesktopModeSupported(@NonNull Context context) {
return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
@@ -68,32 +63,45 @@ public final class DesktopModeHelper {
}
/**
+ * Return {@code true} if the current device can hosts desktop sessions on its internal display.
+ */
+ @VisibleForTesting
+ static boolean canInternalDisplayHostDesktops(@NonNull Context context) {
+ return context.getResources().getBoolean(R.bool.config_canInternalDisplayHostDesktops);
+ }
+
+ /**
* Check if Desktop mode should be enabled because the dev option is shown and enabled.
*/
private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) {
return DesktopModeFlags.isDesktopModeForcedEnabled() && (isDesktopModeDevOptionsSupported(
- context) || isInternalDisplayEligibleToHostDesktops(context));
+ context) || isDeviceEligibleForDesktopMode(context));
}
@VisibleForTesting
- static boolean isInternalDisplayEligibleToHostDesktops(@NonNull Context context) {
- return !shouldEnforceDeviceRestrictions() || canInternalDisplayHostDesktops(context) || (
- Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionsSupported(
- context));
+ static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
+ if (!shouldEnforceDeviceRestrictions()) {
+ return true;
+ }
+ final boolean desktopModeSupported = isDesktopModeSupported(context)
+ && canInternalDisplayHostDesktops(context);
+ final boolean desktopModeSupportedByDevOptions =
+ Flags.enableDesktopModeThroughDevOption()
+ && isDesktopModeDevOptionsSupported(context);
+ return desktopModeSupported || desktopModeSupportedByDevOptions;
}
/**
* Return {@code true} if desktop mode can be entered on the current device.
*/
static boolean canEnterDesktopMode(@NonNull Context context) {
- return (isInternalDisplayEligibleToHostDesktops(context)
- && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue()
- && (isDesktopModeSupported(context) || !shouldEnforceDeviceRestrictions()))
+ return (isDeviceEligibleForDesktopMode(context)
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue())
|| isDesktopModeEnabledByDevOption(context);
}
/** Returns {@code true} if desktop experience wallpaper is supported on this device. */
public static boolean isDeviceEligibleForDesktopExperienceWallpaper(@NonNull Context context) {
- return enableConnectedDisplaysWallpaper() && canEnterDesktopMode(context);
+ return enableConnectedDisplaysWallpaper() && isDeviceEligibleForDesktopMode(context);
}
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index ac987929a142..b6f74a08631e 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -75,8 +75,8 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
return RESULT_SKIP;
}
if (com.android.window.flags.Flags.fixLayoutExistingTask()
- && task.getOrganizedTask() != null) {
- appendLog("task is organized, skipping");
+ && task.getCreatedByOrganizerTask() != null) {
+ appendLog("has created-by-organizer-task, skipping");
return RESULT_SKIP;
}
@@ -111,6 +111,11 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
return RESULT_SKIP;
}
+ if ((options == null || options.getLaunchBounds() == null) && task.hasOverrideBounds()) {
+ appendLog("current task has bounds set, not overriding");
+ return RESULT_SKIP;
+ }
+
DesktopModeBoundsCalculator.updateInitialBounds(task, layout, activity, options,
outParams.mBounds, this::appendLog);
appendLog("final desktop mode task bounds set to %s", outParams.mBounds);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 682f3d8cf1e5..e30c24d87d20 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -108,7 +108,6 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_W
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
@@ -125,7 +124,6 @@ import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
import static com.android.server.wm.DisplayContentProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.DisplayContentProto.MIN_SIZE_OF_RESIZEABLE_TASK_DP;
-import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
@@ -164,6 +162,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -196,7 +195,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.DisplayUtils;
@@ -367,15 +365,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final AppTransition mAppTransition;
- final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
- final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
- final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
- /**
- * If a container is closing when resizing, keeps track of its starting bounds when it is
- * removed from {@link #mChangingContainers}.
- */
- final ArrayMap<WindowContainer, Rect> mClosingChangingContainers = new ArrayMap<>();
private MetricsLogger mMetricsLogger;
@@ -467,6 +457,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private DisplayInfo mLastDisplayInfoOverride;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ @NonNull
private final DisplayPolicy mDisplayPolicy;
private final DisplayRotation mDisplayRotation;
@@ -553,6 +544,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Remove this display when animation on it has completed. */
private boolean mDeferredRemoval;
+ @NonNull
final PinnedTaskController mPinnedTaskController;
private final LinkedList<ActivityRecord> mTmpUpdateAllDrawn = new LinkedList();
@@ -1113,6 +1105,29 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
};
/**
+ * Called to update fields retrieve from {@link #getDisplayUiContext()} resources when
+ * there's a configuration update on {@link #getDisplayUiContext()}.
+ */
+ @NonNull
+ private final ComponentCallbacks mSysUiContextConfigCallback = new ComponentCallbacks() {
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ synchronized (mWmService.mGlobalLock) {
+ if (mDisplayReady) {
+ mDisplayPolicy.onConfigurationChanged();
+ mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
+ }
+ }
+ }
+
+ @Override
+ public void onLowMemory() {
+ // Do nothing.
+ }
+ };
+
+ /**
* Create new {@link DisplayContent} instance, add itself to the root window container and
* initialize direct children.
* @param display May not be null.
@@ -1910,17 +1925,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return false;
}
if (checkOpening) {
- if (mTransitionController.isShellTransitionsEnabled()) {
- if (!mTransitionController.isCollecting(r)) {
- return false;
- }
- } else {
- if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
- // Apply normal rotation animation in case of the activity set different
- // requested orientation without activity switch, or the transition is unset due
- // to starting window was transferred ({@link #mSkipAppTransitionAnimation}).
- return false;
- }
+ if (!mTransitionController.isCollecting(r)) {
+ // Apply normal rotation animation in case the activity changes requested
+ // orientation without activity switch.
+ return false;
}
if (r.isState(RESUMED) && !r.getTask().mInResumeTopActivity) {
// If the activity is executing or has done the lifecycle callback, use normal
@@ -2815,11 +2823,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final int lastOrientation = getConfiguration().orientation;
final int lastWindowingMode = getWindowingMode();
super.onConfigurationChanged(newParentConfig);
- if (mDisplayPolicy != null) {
- mDisplayPolicy.onConfigurationChanged();
- mPinnedTaskController.onPostDisplayConfigurationChanged();
- mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
+ if (!Flags.trackSystemUiContextBeforeWms()) {
+ mSysUiContextConfigCallback.onConfigurationChanged(newParentConfig);
}
+ mPinnedTaskController.onPostDisplayConfigurationChanged();
// Update IME parent if needed.
updateImeParent();
@@ -3239,25 +3246,48 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
Slog.e(TAG, "ShouldShowSystemDecors shouldn't be updated when the flag is off.");
}
- final boolean shouldShow;
- if (isDefaultDisplay) {
- shouldShow = true;
- } else if (isPrivate()) {
- shouldShow = false;
- } else {
- shouldShow = mDisplay.canHostTasks();
+ final boolean shouldShowContent;
+ if (!allowContentModeSwitch()) {
+ return;
}
+ shouldShowContent = mDisplay.canHostTasks();
- if (shouldShow == mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)) {
+ if (shouldShowContent == mWmService.mDisplayWindowSettings
+ .shouldShowSystemDecorsLocked(this)) {
return;
}
- mWmService.mDisplayWindowSettings.setShouldShowSystemDecorsLocked(this, shouldShow);
+ mWmService.mDisplayWindowSettings.setShouldShowSystemDecorsLocked(this, shouldShowContent);
- if (!shouldShow) {
+ if (!shouldShowContent) {
clearAllTasksOnDisplay(null /* clearTasksCallback */, false /* isRemovingDisplay */);
}
}
+ /**
+ * Note that we only allow displays that are able to show system decorations to use the content
+ * mode switch; however, not all displays that are able to show system decorations are allowed
+ * to use the content mode switch.
+ */
+ boolean allowContentModeSwitch() {
+ // The default display should always show system decorations.
+ if (isDefaultDisplay) {
+ return false;
+ }
+
+ // Private display should never show system decorations.
+ if (isPrivate()) {
+ return false;
+ }
+
+ // TODO(b/391965805): Remove this after introducing FLAG_ALLOW_SYSTEM_DECORATIONS_CHANGE.
+ // Virtual displays cannot add or remove system decorations during their lifecycle.
+ if (mDisplay.getType() == Display.TYPE_VIRTUAL) {
+ return false;
+ }
+
+ return true;
+ }
+
DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
return null;
@@ -3354,10 +3384,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void removeImmediately() {
mDeferredRemoval = false;
try {
- // Clear all transitions & screen frozen states when removing display.
- mOpeningApps.clear();
- mClosingApps.clear();
- mChangingContainers.clear();
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
@@ -3380,6 +3406,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
.getKeyguardController().onDisplayRemoved(mDisplayId);
mWallpaperController.resetLargestDisplay(mDisplay);
mWmService.mDisplayWindowSettings.onDisplayRemoved(this);
+ if (Flags.trackSystemUiContextBeforeWms()) {
+ getDisplayUiContext().unregisterComponentCallbacks(mSysUiContextConfigCallback);
+ }
} finally {
mDisplayReady = false;
}
@@ -3549,12 +3578,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
- for (int i = mOpeningApps.size() - 1; i >= 0; i--) {
- mOpeningApps.valueAt(i).writeIdentifierToProto(proto, OPENING_APPS);
- }
- for (int i = mClosingApps.size() - 1; i >= 0; i--) {
- mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
- }
final Task focusedRootTask = getFocusedRootTask();
if (focusedRootTask != null) {
@@ -3822,7 +3845,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (mTmpWindow == null) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d",
getDisplayId());
- return null;
}
return mTmpWindow;
}
@@ -4813,19 +4835,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingContainers.isEmpty()) {
- pw.println();
- if (mOpeningApps.size() > 0) {
- pw.print(" mOpeningApps="); pw.println(mOpeningApps);
- }
- if (mClosingApps.size() > 0) {
- pw.print(" mClosingApps="); pw.println(mClosingApps);
- }
- if (mChangingContainers.size() > 0) {
- pw.print(" mChangingApps="); pw.println(mChangingContainers);
- }
- }
-
mUnknownAppVisibilityController.dump(pw, " ");
}
@@ -5447,7 +5456,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
reconfigureDisplayLocked();
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
- // Attach the SystemUiContext to this DisplayContent the get latest configuration.
+ // Attach the SystemUiContext to this DisplayContent to get latest configuration.
// Note that the SystemUiContext will be removed automatically if this DisplayContent
// is detached.
registerSystemUiContext();
@@ -5455,11 +5464,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
private void registerSystemUiContext() {
+ final Context systemUiContext = getDisplayUiContext();
final WindowProcessController wpc = mAtmService.getProcessController(
- getDisplayUiContext().getIApplicationThread());
+ systemUiContext.getIApplicationThread());
mWmService.mWindowContextListenerController.registerWindowContainerListener(
- wpc, getDisplayUiContext().getWindowContextToken(), this,
+ wpc, systemUiContext.getWindowContextToken(), this,
INVALID_WINDOW_TYPE, null /* options */);
+ if (Flags.trackSystemUiContextBeforeWms()) {
+ systemUiContext.registerComponentCallbacks(mSysUiContextConfigCallback);
+ }
}
@Override
@@ -6578,22 +6591,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
.getKeyguardController().isKeyguardLocked(mDisplayId);
}
- boolean isKeyguardLockedOrAodShowing() {
- return isKeyguardLocked() || isAodShowing();
- }
-
- /**
- * @return whether aod is showing for this display
- */
- boolean isAodShowing() {
- final boolean isAodShowing = mRootWindowContainer.mTaskSupervisor
- .getKeyguardController().isAodShowing(mDisplayId);
- if (mDisplayId == DEFAULT_DISPLAY && isAodShowing) {
- return !isKeyguardGoingAway();
- }
- return isAodShowing;
- }
-
/**
* @return whether keyguard is going away on this display
*/
@@ -6654,6 +6651,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
}
+ @NonNull
Context getDisplayUiContext() {
return mDisplayPolicy.getSystemUiContext();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 10f591cfd379..fbe850198c50 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1865,6 +1865,7 @@ public class DisplayPolicy {
return mContext;
}
+ @NonNull
Context getSystemUiContext() {
return mUiContext;
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 539fc123720e..117387553f30 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -247,12 +247,7 @@ class DisplayWindowSettings {
void setShouldShowSystemDecorsLocked(@NonNull DisplayContent dc, boolean shouldShow) {
final boolean changed = (shouldShow != shouldShowSystemDecorsLocked(dc));
-
- final DisplayInfo displayInfo = dc.getDisplayInfo();
- final SettingsProvider.SettingsEntry overrideSettings =
- mSettingsProvider.getOverrideSettings(displayInfo);
- overrideSettings.mShouldShowSystemDecors = shouldShow;
- mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ setShouldShowSystemDecorsInternalLocked(dc, shouldShow);
if (enableDisplayContentModeManagement()) {
if (dc.isDefaultDisplay || dc.isPrivate() || !changed) {
@@ -269,6 +264,15 @@ class DisplayWindowSettings {
}
}
+ void setShouldShowSystemDecorsInternalLocked(@NonNull DisplayContent dc,
+ boolean shouldShow) {
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+ final SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getOverrideSettings(displayInfo);
+ overrideSettings.mShouldShowSystemDecors = shouldShow;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
boolean isHomeSupportedLocked(@NonNull DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
// Default display should show home.
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 69f32cb7b8ea..84281b8fbecf 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -122,7 +122,7 @@ class DragState {
float mThumbOffsetX, mThumbOffsetY;
InputInterceptor mInputInterceptor;
ArrayList<WindowState> mNotifiedWindows;
- boolean mDragInProgress;
+ private boolean mDragInProgress;
// Set to non -1 value if a valid app requests DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START
int mCallingTaskIdToHide;
/**
@@ -161,7 +161,7 @@ class DragState {
private boolean mIsClosing;
// Stores the last drop event which was reported to a valid drop target window, or null
- // otherwise. This drop event will contain private info and should only be consumed by the
+ // otherwise. This drop event will contain private info and should only be consumed by the
// unhandled drag listener.
DragEvent mUnhandledDropEvent;
@@ -243,7 +243,7 @@ class DragState {
for (WindowState ws : mNotifiedWindows) {
float inWindowX = 0;
float inWindowY = 0;
- SurfaceControl dragSurface = null;
+ boolean includeDragSurface = false;
if (!mDragResult && (ws.mSession.mPid == mPid)) {
// Report unconsumed drop location back to the app that started the drag.
inWindowX = ws.translateToWindowX(mCurrentDisplayX);
@@ -251,13 +251,10 @@ class DragState {
if (relinquishDragSurfaceToDragSource()) {
// If requested (and allowed), report the drag surface back to the app
// starting the drag to handle the return animation
- dragSurface = mSurfaceControl;
+ includeDragSurface = true;
}
}
- DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, inWindowX,
- inWindowY, mThumbOffsetX, mThumbOffsetY,
- mCurrentDisplayContent.getDisplayId(), mFlags, null, null, null,
- dragSurface, null, mDragResult);
+ DragEvent event = obtainDragEndedEvent(inWindowX, inWindowY, includeDragSurface);
try {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DRAG_ENDED to " + ws);
ws.mClient.dispatchDragEvent(event);
@@ -310,10 +307,10 @@ class DragState {
/**
* Creates the drop event for dispatching to the unhandled drag.
- * TODO(b/384841906): Update `inWindowX` and `inWindowY` to be display-coordinate.
*/
- private DragEvent createUnhandledDropEvent(float inWindowX, float inWindowY) {
- return obtainDragEvent(DragEvent.ACTION_DROP, inWindowX, inWindowY, mDataDescription, mData,
+ private DragEvent createUnhandledDropEvent(float inDisplayX, float inDisplayY) {
+ return obtainDragEvent(DragEvent.ACTION_DROP, inDisplayX, inDisplayY, mDataDescription,
+ mData,
/* includeDragSurface= */ true,
/* includeDragFlags= */ true, null /* dragAndDropPermissions */);
}
@@ -370,11 +367,8 @@ class DragState {
}
final WindowState touchedWin = mService.mInputToWindowMap.get(token);
- // TODO(b/384841906): The x, y here when sent to a window and unhandled, will still be
- // relative to the window it was originally sent to. Need to update this to actually be
- // display-coordinate.
- final DragEvent unhandledDropEvent = createUnhandledDropEvent(inWindowX, inWindowY);
if (!isWindowNotified(touchedWin)) {
+ final DragEvent unhandledDropEvent = createUnhandledDropEvent(inWindowX, inWindowY);
// Delegate to the unhandled drag listener as a first pass
if (mDragDropController.notifyUnhandledDrop(unhandledDropEvent, "unhandled-drop")) {
// The unhandled drag listener will call back to notify whether it has consumed
@@ -392,6 +386,8 @@ class DragState {
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to " + touchedWin);
+ final DragEvent unhandledDropEvent = createUnhandledDropEvent(
+ touchedWin.getBounds().left + inWindowX, touchedWin.getBounds().top + inWindowY);
final IBinder clientToken = touchedWin.mClient.asBinder();
final DragEvent event = createDropEvent(inWindowX, inWindowY, touchedWin);
@@ -776,28 +772,37 @@ class DragState {
displayId, (int) (displayX - mThumbOffsetX), (int) (displayY - mThumbOffsetY));
}
- /**
- * Returns true if it has sent DRAG_STARTED broadcast out but has not been sent DRAG_END
- * broadcast.
- */
- boolean isInProgress() {
- return mDragInProgress;
+ private DragEvent obtainDragEndedEvent(float x, float y, boolean includeDragSurface) {
+ return obtainDragEvent(DragEvent.ACTION_DRAG_ENDED, x, y, /* description= */
+ null, /* data= */ null, includeDragSurface, /* includeDragFlags= */
+ true, /* dragAndDropPermissions= */ null, mDragResult);
+ }
+
+ private DragEvent obtainDragEvent(int action, float x, float y, ClipDescription description,
+ ClipData data, boolean includeDragSurface, boolean includeDragFlags,
+ IDragAndDropPermissions dragAndDropPermissions) {
+ return obtainDragEvent(action, x, y, description, data, includeDragSurface,
+ includeDragFlags, dragAndDropPermissions, /* dragResult= */ false);
}
/**
* `x` and `y` here varies between local window coordinate, relative coordinate to another
* window and local display coordinate, all depending on the `action`. Please take a look
* at the callers to determine the type.
- * TODO(b/384845022): Properly document the events sent based on the event type.
+ * - ACTION_DRAG_STARTED: (x, y) is relative coordinate to the target window's origin
+ * (possible to have negative values).
+ * - ACTION_DROP:
+ * --- UnhandledDropEvent: (x, y) is in display space coordinate.
+ * --- DropEvent: (x, y) is in local window coordinate where event is targeted to.
+ * - ACTION_DRAG_ENDED: (x, y) is in local window coordinate where event is targeted to.
*/
private DragEvent obtainDragEvent(int action, float x, float y, ClipDescription description,
ClipData data, boolean includeDragSurface, boolean includeDragFlags,
- IDragAndDropPermissions dragAndDropPermissions) {
+ IDragAndDropPermissions dragAndDropPermissions, boolean dragResult) {
return DragEvent.obtain(action, x, y, mThumbOffsetX, mThumbOffsetY,
mCurrentDisplayContent.getDisplayId(), includeDragFlags ? mFlags : 0,
null /* localState */, description, data,
- includeDragSurface ? mSurfaceControl : null, dragAndDropPermissions,
- false /* result */);
+ includeDragSurface ? mSurfaceControl : null, dragAndDropPermissions, dragResult);
}
private ValueAnimator createReturnAnimationLocked() {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index dd2f49e171a8..6091b8334438 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
@@ -217,9 +216,6 @@ class KeyguardController {
} else if (keyguardShowing && !state.mKeyguardShowing) {
transition.addFlag(TRANSIT_FLAG_KEYGUARD_APPEARING);
}
- if (mWindowManager.mFlags.mAodTransition && aodShowing && !state.mAodShowing) {
- transition.addFlag(TRANSIT_FLAG_AOD_APPEARING);
- }
}
}
// Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -242,27 +238,19 @@ class KeyguardController {
state.mAodShowing = aodShowing;
state.writeEventLog("setKeyguardShown");
- if (keyguardChanged || aodChanged) {
- if (keyguardChanged) {
- // Irrelevant to AOD.
- state.mKeyguardGoingAway = false;
- if (keyguardShowing) {
- state.mDismissalRequested = false;
- }
+ if (keyguardChanged) {
+ // Irrelevant to AOD.
+ state.mKeyguardGoingAway = false;
+ if (keyguardShowing) {
+ state.mDismissalRequested = false;
}
if (goingAwayRemoved
- || (keyguardShowing && !Display.isOffState(dc.getDisplayInfo().state))
- || (mWindowManager.mFlags.mAodTransition && aodShowing)) {
+ || (keyguardShowing && !Display.isOffState(dc.getDisplayInfo().state))) {
// Keyguard decided to show or stopped going away. Send a transition to animate back
// to the locked state before holding the sleep token again
if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
dc.requestTransitionAndLegacyPrepare(
TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING);
- if (mWindowManager.mFlags.mAodTransition && aodShowing
- && dc.mTransitionController.isCollecting()) {
- dc.mTransitionController.getCollectingTransition().addFlag(
- TRANSIT_FLAG_AOD_APPEARING);
- }
}
dc.mWallpaperController.adjustWallpaperWindows();
dc.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/PresentationController.java b/services/core/java/com/android/server/wm/PresentationController.java
index 69463433827f..b3cff9c6cc3d 100644
--- a/services/core/java/com/android/server/wm/PresentationController.java
+++ b/services/core/java/com/android/server/wm/PresentationController.java
@@ -56,11 +56,6 @@ class PresentationController {
ProtoLog.v(WmProtoLogGroups.WM_DEBUG_PRESENTATION, "Presentation added to display %d: %s",
win.getDisplayId(), win);
mPresentingDisplayIds.add(win.getDisplayId());
- if (enablePresentationForConnectedDisplays()) {
- // A presentation hides all activities behind on the same display.
- win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
- /*notifyClients=*/ true);
- }
win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ true);
}
@@ -76,11 +71,6 @@ class PresentationController {
if (displayIdIndex != -1) {
mPresentingDisplayIds.remove(displayIdIndex);
}
- if (enablePresentationForConnectedDisplays()) {
- // A presentation hides all activities behind on the same display.
- win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
- /*notifyClients=*/ true);
- }
win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ false);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c93efd327096..40f16c187f20 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2775,6 +2775,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return;
}
+ if (enableDisplayContentModeManagement() && display.allowContentModeSwitch()) {
+ mWindowManager.mDisplayWindowSettings
+ .setShouldShowSystemDecorsInternalLocked(display,
+ display.mDisplay.canHostTasks());
+ }
+
startSystemDecorations(display, "displayAdded");
// Drop any cached DisplayInfos associated with this display id - the values are now
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3abab8bf62c2..988af44f6e55 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -166,6 +166,7 @@ import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.ITaskOrganizer;
import android.window.PictureInPictureSurfaceTransaction;
@@ -503,6 +504,17 @@ class Task extends TaskFragment {
int mOffsetYForInsets;
/**
+ * When set to true, the task will be kept at a PERCEPTIBLE_APP_ADJ, and downgraded
+ * to PREVIOUS_APP_ADJ if not in foreground for a period of time.
+ * One example use case is for desktop form factors, where it is important keep tasks in the
+ * perceptible state (rather than cached where it may be frozen) when a user moves it to the
+ * foreground.
+ * On startup, restored Tasks will not be perceptible, until user actually interacts with it
+ * (i.e. brings it to the foreground)
+ */
+ boolean mIsPerceptible = false;
+
+ /**
* Whether the compatibility overrides that change the resizability of the app should be allowed
* for the specific app.
*/
@@ -2027,7 +2039,7 @@ class Task extends TaskFragment {
}
if (shouldStartChangeTransition(prevWinMode, mTmpPrevBounds)) {
- initializeChangeTransition(mTmpPrevBounds);
+ mTransitionController.collectVisibleChange(this);
}
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
@@ -2378,7 +2390,7 @@ class Task extends TaskFragment {
// configurations and let its parent (organized task) to control it;
final Task rootTask = getRootTask();
boolean shouldInheritBounds = rootTask != this && rootTask.isOrganized();
- if (Flags.enableMultipleDesktopsBackend()) {
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
// Only inherit from organized parent when this task is not organized.
shouldInheritBounds &= !isOrganized();
}
@@ -3853,6 +3865,7 @@ class Task extends TaskFragment {
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
pw.print(" isResizeable="); pw.println(isResizeable());
+ pw.print(" isPerceptible="); pw.println(mIsPerceptible);
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 74059c1cc9b1..bbda849262b2 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1910,7 +1910,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (!hasDirectChildActivities()) {
return false;
}
- if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) {
+ if (mResumedActivity != null && !mResumedActivity.finishing
+ && mTransitionController.isTransientLaunch(mResumedActivity)) {
// Even if the transient activity is occluded, defer pausing (addToStopping will still
// be called) it until the transient transition is done. So the current resuming
// activity won't need to wait for additional pause complete.
@@ -2779,9 +2780,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
// If this TaskFragment is closing while resizing, crop to the starting bounds instead.
- final Rect bounds = isClosingWhenResizing()
- ? mDisplayContent.mClosingChangingContainers.get(this)
- : getBounds();
+ final Rect bounds = getBounds();
final int width = bounds.width();
final int height = bounds.height();
if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index e3a5b66b83fd..6c7d979dc43d 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -384,7 +384,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// an existing task.
adjustBoundsToAvoidConflictInDisplayArea(taskDisplayArea, outParams.mBounds);
}
- } else {
+ } else if (task == null || !task.hasOverrideBounds()) {
if (source != null && source.inFreeformWindowingMode()
&& resolvedMode == WINDOWING_MODE_FREEFORM
&& outParams.mBounds.isEmpty()
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index fe653e454d6c..5217a759c6ae 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -36,7 +36,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_FLAG_AOD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -974,10 +973,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
- boolean isInAodAppearTransition() {
- return (mFlags & TRANSIT_FLAG_AOD_APPEARING) != 0;
- }
-
/**
* Specifies configuration change explicitly for the window container, so it can be chosen as
* transition target. This is usually used with transition mode
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 25b513d85384..ba7f36419ac5 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -525,19 +525,6 @@ class TransitionController {
return false;
}
- boolean isInAodAppearTransition() {
- if (mCollectingTransition != null && mCollectingTransition.isInAodAppearTransition()) {
- return true;
- }
- for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
- if (mWaitingTransitions.get(i).isInAodAppearTransition()) return true;
- }
- for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
- if (mPlayingTransitions.get(i).isInAodAppearTransition()) return true;
- }
- return false;
- }
-
/**
* @return A pair of the transition and restore-behind target for the given {@param container}.
* @param container An ancestor of a transient-launch activity
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 70948e1264c4..a8b9fedcdc73 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -166,14 +166,6 @@ class WallpaperController {
mFindResults.setWallpaperTarget(w);
return false;
}
- } else if (mService.mFlags.mAodTransition
- && mDisplayContent.isKeyguardLockedOrAodShowing()) {
- if (mService.mPolicy.isKeyguardHostWindow(w.mAttrs)
- && w.mTransitionController.isInAodAppearTransition()) {
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Found aod transition wallpaper target: " + w);
- mFindResults.setWallpaperTarget(w);
- return true;
- }
}
final boolean animationWallpaper = animatingContainer != null
@@ -300,12 +292,6 @@ class WallpaperController {
return false;
}
- boolean isWallpaperTargetAnimating() {
- return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS)
- && (mWallpaperTarget.mActivityRecord == null
- || !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart());
- }
-
void hideDeferredWallpapersIfNeededLegacy() {
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
@@ -692,8 +678,7 @@ class WallpaperController {
private WallpaperWindowToken getTokenForTarget(WindowState target) {
if (target == null) return null;
WindowState window = mFindResults.getTopWallpaper(
- (target.canShowWhenLocked() && mService.isKeyguardLocked())
- || (mService.mFlags.mAodTransition && mDisplayContent.isAodShowing()));
+ target.canShowWhenLocked() && mService.isKeyguardLocked());
return window == null ? null : window.mToken.asWallpaperToken();
}
@@ -736,9 +721,7 @@ class WallpaperController {
if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
mFindResults.setWallpaperTarget(
- mFindResults.getTopWallpaper(mService.mFlags.mAodTransition
- ? mDisplayContent.isKeyguardLockedOrAodShowing()
- : mDisplayContent.isKeyguardLocked()));
+ mFindResults.getTopWallpaper(mDisplayContent.isKeyguardLocked()));
}
}
@@ -848,14 +831,6 @@ class WallpaperController {
// Use the old target if new target is hidden but old target
// is not. If they're both hidden, still use the new target.
mWallpaperTarget = prevWallpaperTarget;
- } else if (newTargetHidden == oldTargetHidden
- && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mActivityRecord)
- && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mActivityRecord)
- || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mActivityRecord))) {
- // If they're both hidden (or both not hidden), prefer the one that's currently in
- // opening or closing app list, this allows transition selection logic to better
- // determine the wallpaper status of opening/closing apps.
- mWallpaperTarget = prevWallpaperTarget;
}
result.setWallpaperTarget(wallpaperTarget);
@@ -910,17 +885,11 @@ class WallpaperController {
if (mDisplayContent.mWmService.mFlags.mEnsureWallpaperInTransitions) {
visibleRequested = mWallpaperTarget != null && mWallpaperTarget.isVisibleRequested();
}
- updateWallpaperTokens(visibleRequested,
- mService.mFlags.mAodTransition
- ? mDisplayContent.isKeyguardLockedOrAodShowing()
- : mDisplayContent.isKeyguardLocked());
+ updateWallpaperTokens(visibleRequested, mDisplayContent.isKeyguardLocked());
ProtoLog.v(WM_DEBUG_WALLPAPER,
"Wallpaper at display %d - visibility: %b, keyguardLocked: %b",
- mDisplayContent.getDisplayId(), visible,
- mService.mFlags.mAodTransition
- ? mDisplayContent.isKeyguardLockedOrAodShowing()
- : mDisplayContent.isKeyguardLocked());
+ mDisplayContent.getDisplayId(), visible, mDisplayContent.isKeyguardLocked());
if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 95cdf46ea488..45202a29ba97 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -34,7 +34,6 @@ import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static android.view.WindowInsets.Type.InsetsType;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
@@ -901,10 +900,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
@CallSuper
void removeImmediately() {
- final DisplayContent dc = getDisplayContent();
- if (dc != null) {
- dc.mClosingChangingContainers.remove(this);
- }
while (!mChildren.isEmpty()) {
final E child = mChildren.getLast();
child.removeImmediately();
@@ -1116,10 +1111,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (asWindowState() == null) {
mTransitionController.collect(this);
}
- // Cancel any change transition queued-up for this container on the old display when
- // this container is moved from the old display.
- mDisplayContent.mClosingChangingContainers.remove(this);
- mDisplayContent.mChangingContainers.remove(this);
}
mDisplayContent = dc;
if (dc != null && dc != this && mPendingTransaction != null) {
@@ -1268,14 +1259,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
- * @return {@code true} when the container is waiting the app transition start, {@code false}
- * otherwise.
- */
- boolean isWaitingForTransitionStart() {
- return false;
- }
-
- /**
* @return {@code true} if in this subtree of the hierarchy we have an
* {@code ActivityRecord#isAnimating(TRANSITION)}, {@code false} otherwise.
*/
@@ -1302,13 +1285,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return isAnimating(0 /* self only */);
}
- /**
- * @return {@code true} if the container is in changing app transition.
- */
- boolean isChangingAppTransition() {
- return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
- }
-
boolean inTransition() {
return mTransitionController.inTransition(this);
}
@@ -1427,12 +1403,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return setVisibleRequested(newVisReq);
}
- /** Whether this window is closing while resizing. */
- boolean isClosingWhenResizing() {
- return mDisplayContent != null
- && mDisplayContent.mClosingChangingContainers.containsKey(this);
- }
-
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
@@ -3044,36 +3014,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|| (getParent() != null && getParent().inPinnedWindowingMode());
}
- /**
- * Initializes a change transition.
- *
- * For now, this will only be called for the following cases:
- * 1. {@link Task} is changing windowing mode between fullscreen and freeform.
- * 2. {@link TaskFragment} is organized and is changing window bounds.
- * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. (The
- * transition will happen on the {@link TaskFragment} for this case).
- *
- * This shouldn't be called on other {@link WindowContainer} unless there is a valid
- * use case.
- *
- * @param startBounds The original bounds (on screen) of the surface we are snapshotting.
- */
- void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
- if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
- mDisplayContent.mTransitionController.collectVisibleChange(this);
- return;
- }
- mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
- mDisplayContent.mChangingContainers.add(this);
- // Calculate the relative position in parent container.
- final Rect parentBounds = getParent().getBounds();
- mTmpPoint.set(startBounds.left - parentBounds.left, startBounds.top - parentBounds.top);
- }
-
- void initializeChangeTransition(Rect startBounds) {
- initializeChangeTransition(startBounds, null /* freezeTarget */);
- }
-
ArraySet<WindowContainer> getAnimationSources() {
return mSurfaceAnimationSources;
}
@@ -3166,8 +3106,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
getAnimationPosition(mTmpPoint);
mTmpRect.offsetTo(0, 0);
- final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
- && isChangingAppTransition();
+ final boolean isChanging = AppTransition.isChangeTransitOld(transit);
if (isChanging) {
final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -3519,9 +3458,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
&& (mSurfaceAnimator.getAnimationType() & typesToCheck) > 0) {
return true;
}
- if ((flags & TRANSITION) != 0 && isWaitingForTransitionStart()) {
- return true;
- }
return false;
}
@@ -3603,13 +3539,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return;
}
- if (isClosingWhenResizing()) {
- // This container is closing while resizing, keep its surface at the starting position
- // to prevent animation flicker.
- getRelativePosition(mDisplayContent.mClosingChangingContainers.get(this), mTmpPos);
- } else {
- getRelativePosition(mTmpPos);
- }
+ getRelativePosition(mTmpPos);
final int deltaRotation = getRelativeDisplayRotation();
if (mTmpPos.equals(mLastSurfacePosition) && deltaRotation == mLastDeltaRotation) {
return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d699a689459e..8aed91b2dc66 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -157,6 +157,7 @@ import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY;
import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.setScPropertiesInClient;
@@ -1820,125 +1821,158 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win);
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
- boolean imMayMove = true;
-
- win.mToken.addWindow(win);
- displayPolicy.addWindowLw(win, attrs);
- displayPolicy.setDropInputModePolicy(win, win.mAttrs);
- if (type == TYPE_APPLICATION_STARTING && activity != null) {
- activity.attachStartingWindow(win);
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
- activity, win);
- } else if (type == TYPE_INPUT_METHOD
- // IME window is always touchable.
- // Ignore non-touchable windows e.g. Stylus InkWindow.java.
- && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) {
- displayContent.setInputMethodWindowLocked(win);
- imMayMove = false;
- } else if (type == TYPE_INPUT_METHOD_DIALOG) {
- displayContent.computeImeTarget(true /* updateImeTarget */);
- imMayMove = false;
- } else {
- if (type == TYPE_WALLPAPER) {
- displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
- displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- } else if (win.hasWallpaper()) {
- displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
- // If there is currently a wallpaper being shown, and
- // the base layer of the new window is below the current
- // layer of the target window, then adjust the wallpaper.
- // This is to avoid a new window being placed between the
- // wallpaper and its target.
- displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ // Only a presentation window needs a transition because its visibility affets the
+ // lifecycle of apps below (b/390481865).
+ if (enablePresentationForConnectedDisplays() && win.isPresentation()) {
+ Transition transition = null;
+ if (!win.mTransitionController.isCollecting()) {
+ transition = win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN);
+ }
+ win.mTransitionController.collect(win.mToken);
+ res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
+ outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs);
+ // A presentation hides all activities behind on the same display.
+ win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
+ /*notifyClients=*/ true);
+ win.mTransitionController.getCollectingTransition().setReady(win.mToken, true);
+ if (transition != null) {
+ win.mTransitionController.requestStartTransition(transition, null,
+ null /* remoteTransition */, null /* displayChange */);
}
+ } else {
+ res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
+ outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs);
}
+ }
- final WindowStateAnimator winAnimator = win.mWinAnimator;
- winAnimator.mEnterAnimationPending = true;
- winAnimator.mEnteringAnimation = true;
+ Binder.restoreCallingIdentity(origId);
- if (displayPolicy.areSystemBarsForcedConsumedLw()) {
- res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
- }
- if (displayContent.isInTouchMode()) {
- res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
- }
- if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
- res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+ return res;
+ }
+
+ private int addWindowInner(@NonNull WindowState win, @NonNull DisplayPolicy displayPolicy,
+ @NonNull ActivityRecord activity, @NonNull DisplayContent displayContent,
+ @NonNull InsetsState outInsetsState, @NonNull Rect outAttachedFrame,
+ @NonNull InsetsSourceControl.Array outActiveControls, @NonNull IWindow client,
+ @NonNull float[] outSizeCompatScale, @NonNull LayoutParams attrs) {
+ int res = 0;
+ final int type = attrs.type;
+ boolean imMayMove = true;
+
+ win.mToken.addWindow(win);
+ displayPolicy.addWindowLw(win, attrs);
+ displayPolicy.setDropInputModePolicy(win, win.mAttrs);
+ if (type == TYPE_APPLICATION_STARTING && activity != null) {
+ activity.attachStartingWindow(win);
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
+ activity, win);
+ } else if (type == TYPE_INPUT_METHOD
+ // IME window is always touchable.
+ // Ignore non-touchable windows e.g. Stylus InkWindow.java.
+ && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) {
+ displayContent.setInputMethodWindowLocked(win);
+ imMayMove = false;
+ } else if (type == TYPE_INPUT_METHOD_DIALOG) {
+ displayContent.computeImeTarget(true /* updateImeTarget */);
+ imMayMove = false;
+ } else {
+ if (type == TYPE_WALLPAPER) {
+ displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ } else if (win.hasWallpaper()) {
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
+ // If there is currently a wallpaper being shown, and
+ // the base layer of the new window is below the current
+ // layer of the target window, then adjust the wallpaper.
+ // This is to avoid a new window being placed between the
+ // wallpaper and its target.
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
+ }
- displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
+ final WindowStateAnimator winAnimator = win.mWinAnimator;
+ winAnimator.mEnterAnimationPending = true;
+ winAnimator.mEnteringAnimation = true;
- boolean focusChanged = false;
- if (win.canReceiveKeys()) {
- focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
- false /*updateInputWindows*/);
- if (focusChanged) {
- imMayMove = false;
- }
- }
+ if (displayPolicy.areSystemBarsForcedConsumedLw()) {
+ res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
+ }
+ if (displayContent.isInTouchMode()) {
+ res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
+ }
+ if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
+ res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+ }
- if (imMayMove) {
- displayContent.computeImeTarget(true /* updateImeTarget */);
- if (win.isImeOverlayLayeringTarget()) {
- dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type,
- win.isVisibleRequestedOrAdding(), false /* removed */,
- displayContent.getDisplayId());
- }
- }
+ displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
- // Don't do layout here, the window must call
- // relayout to be displayed, so we'll do it there.
- if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) {
- // Assign child layers from the parent Task if the Activity is embedded.
- win.getTask().assignChildLayers();
- } else {
- win.getParent().assignChildLayers();
+ boolean focusChanged = false;
+ if (win.canReceiveKeys()) {
+ focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
+ false /*updateInputWindows*/);
+ if (focusChanged) {
+ imMayMove = false;
}
+ }
- if (focusChanged) {
- displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
- false /*updateInputWindows*/);
+ if (imMayMove) {
+ displayContent.computeImeTarget(true /* updateImeTarget */);
+ if (win.isImeOverlayLayeringTarget()) {
+ dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type,
+ win.isVisibleRequestedOrAdding(), false /* removed */,
+ displayContent.getDisplayId());
}
- displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
+ }
- ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
- + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
+ // Don't do layout here, the window must call
+ // relayout to be displayed, so we'll do it there.
+ if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) {
+ // Assign child layers from the parent Task if the Activity is embedded.
+ win.getTask().assignChildLayers();
+ } else {
+ win.getParent().assignChildLayers();
+ }
- boolean needToSendNewConfiguration =
- win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
- if (win.providesDisplayDecorInsets()) {
- needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
- }
- if (needToSendNewConfiguration) {
- displayContent.sendNewConfiguration();
- }
+ if (focusChanged) {
+ displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
+ false /*updateInputWindows*/);
+ }
+ displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
- // This window doesn't have a frame yet. Don't let this window cause the insets change.
- displayContent.getInsetsStateController().updateAboveInsetsState(
- false /* notifyInsetsChanged */);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
- win.fillInsetsState(outInsetsState, true /* copySources */);
- getInsetsSourceControls(win, outActiveControls);
+ boolean needToSendNewConfiguration =
+ win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
+ if (win.providesDisplayDecorInsets()) {
+ needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
+ }
+ if (needToSendNewConfiguration) {
+ displayContent.sendNewConfiguration();
+ }
- if (win.mLayoutAttached) {
- outAttachedFrame.set(win.getParentWindow().getFrame());
- if (win.mInvGlobalScale != 1f) {
- outAttachedFrame.scale(win.mInvGlobalScale);
- }
- } else {
- // Make this invalid which indicates a null attached frame.
- outAttachedFrame.set(0, 0, -1, -1);
- }
- outSizeCompatScale[0] = win.getCompatScaleForClient();
+ // This window doesn't have a frame yet. Don't let this window cause the insets change.
+ displayContent.getInsetsStateController().updateAboveInsetsState(
+ false /* notifyInsetsChanged */);
+
+ win.fillInsetsState(outInsetsState, true /* copySources */);
+ getInsetsSourceControls(win, outActiveControls);
- if (res >= ADD_OKAY && win.isPresentation()) {
- mPresentationController.onPresentationAdded(win);
+ if (win.mLayoutAttached) {
+ outAttachedFrame.set(win.getParentWindow().getFrame());
+ if (win.mInvGlobalScale != 1f) {
+ outAttachedFrame.scale(win.mInvGlobalScale);
}
+ } else {
+ // Make this invalid which indicates a null attached frame.
+ outAttachedFrame.set(0, 0, -1, -1);
}
+ outSizeCompatScale[0] = win.getCompatScaleForClient();
- Binder.restoreCallingIdentity(origId);
+ if (res >= ADD_OKAY && win.isPresentation()) {
+ mPresentationController.onPresentationAdded(win);
+ }
return res;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4c53ba53a506..4b4736ec6c59 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1060,7 +1060,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
effects |= applyChanges(taskFragment, c);
if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
- taskFragment.initializeChangeTransition(mTmpBounds0);
+ mTransitionController.collectVisibleChange(taskFragment);
}
taskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
return effects;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 30dde543b9d4..b2d28a369edc 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -37,6 +37,7 @@ import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -344,6 +345,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ /**
+ * The most recent timestamp of when one of this process's stopped activities in a
+ * perceptible task became stopped. Written by window manager and read by activity manager.
+ */
+ private volatile long mPerceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm,
@NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
@NonNull WindowProcessListener listener) {
@@ -1228,6 +1235,17 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mActivityStateFlags;
}
+ /**
+ * Returns the most recent timestamp when one of this process's stopped activities in a
+ * perceptible task became stopped. It should only be called if {@link #hasActivities}
+ * returns {@code true} and {@link #getActivityStateFlags} does not have any of
+ * the ACTIVITY_STATE_FLAG_IS_(VISIBLE|PAUSING_OR_PAUSED|STOPPING) bit set.
+ */
+ @HotPath(caller = HotPath.OOM_ADJUSTMENT)
+ public long getPerceptibleTaskStoppedTimeMillis() {
+ return mPerceptibleTaskStoppedTimeMillis;
+ }
+
void computeProcessActivityState() {
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
@@ -1239,6 +1257,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
int minTaskLayer = Integer.MAX_VALUE;
int stateFlags = 0;
int nonOccludedRatio = 0;
+ long perceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
final boolean wasResumed = hasResumedActivity();
final boolean wasAnyVisible = (mActivityStateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
@@ -1287,6 +1306,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
bestInvisibleState = STOPPING;
// Not "finishing" if any of activity isn't finishing.
allStoppingFinishing &= r.finishing;
+ } else if (bestInvisibleState == DESTROYED && r.isState(STOPPED)) {
+ if (task.mIsPerceptible) {
+ perceptibleTaskStoppedTimeMillis =
+ Long.max(r.mStoppedTime, perceptibleTaskStoppedTimeMillis);
+ }
}
}
}
@@ -1324,6 +1348,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
mActivityStateFlags = stateFlags;
+ mPerceptibleTaskStoppedTimeMillis = perceptibleTaskStoppedTimeMillis;
final boolean anyVisible = (stateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 589724182980..92ad2cec364b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -95,6 +95,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
@@ -182,6 +183,7 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
import android.annotation.CallSuper;
@@ -2297,11 +2299,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
dc.updateImeInputAndControlTarget(null);
}
- final int type = mAttrs.type;
-
- if (isPresentation()) {
- mWmService.mPresentationController.onPresentationRemoved(this);
- }
// Check if window provides non decor insets before clearing its provided insets.
final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
@@ -2442,11 +2439,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- removeImmediately();
- mWmService.updateFocusedWindowLocked(isFocused()
- ? UPDATE_FOCUS_REMOVING_FOCUS
- : UPDATE_FOCUS_NORMAL,
- true /*updateInputWindows*/);
+ // Only a presentation window needs a transition because its visibility affets the
+ // lifecycle of apps below (b/390481865).
+ if (enablePresentationForConnectedDisplays() && isPresentation()) {
+ Transition transition = null;
+ if (!mTransitionController.isCollecting()) {
+ transition = mTransitionController.createAndStartCollecting(TRANSIT_CLOSE);
+ }
+ mTransitionController.collect(mToken);
+ mAnimatingExit = true;
+ mRemoveOnExit = true;
+ mToken.setVisibleRequested(false);
+ mWmService.mPresentationController.onPresentationRemoved(this);
+ // A presentation hides all activities behind on the same display.
+ mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
+ /*notifyClients=*/ true);
+ mTransitionController.getCollectingTransition().setReady(mToken, true);
+ if (transition != null) {
+ mTransitionController.requestStartTransition(transition, null,
+ null /* remoteTransition */, null /* displayChange */);
+ }
+ } else {
+ removeImmediately();
+ mWmService.updateFocusedWindowLocked(isFocused()
+ ? UPDATE_FOCUS_REMOVING_FOCUS
+ : UPDATE_FOCUS_NORMAL,
+ true /*updateInputWindows*/);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3272,13 +3291,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mDestroying = false;
destroyedSomething = true;
}
-
- // Since mDestroying will affect ActivityRecord#allDrawn, we need to perform another
- // traversal in case we are waiting on this window to start the transition.
- if (getDisplayContent().mAppTransition.isTransitionSet()
- && getDisplayContent().mOpeningApps.contains(mActivityRecord)) {
- mWmService.mWindowPlacerLocked.requestTraversal();
- }
}
return destroyedSomething;
diff --git a/services/core/java/com/android/server/wm/utils/WindowStyleCache.java b/services/core/java/com/android/server/wm/utils/WindowStyleCache.java
new file mode 100644
index 000000000000..a253c2c75aa2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/WindowStyleCache.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import android.content.res.TypedArray;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.policy.AttributeCache;
+
+import java.util.function.Function;
+
+/**
+ * A wrapper of AttributeCache to preserve more dedicated style caches.
+ * @param <T> The type of style cache.
+ */
+public class WindowStyleCache<T> {
+ @GuardedBy("itself")
+ private final ArrayMap<String, SparseArray<T>> mCache = new ArrayMap<>();
+ private final Function<TypedArray, T> mEntryFactory;
+
+ public WindowStyleCache(Function<TypedArray, T> entryFactory) {
+ mEntryFactory = entryFactory;
+ }
+
+ /** Returns the cached entry. */
+ public T get(String packageName, int theme, int userId) {
+ SparseArray<T> themeMap;
+ synchronized (mCache) {
+ themeMap = mCache.get(packageName);
+ if (themeMap != null) {
+ T style = themeMap.get(theme);
+ if (style != null) {
+ return style;
+ }
+ }
+ }
+
+ final AttributeCache attributeCache = AttributeCache.instance();
+ if (attributeCache == null) {
+ return null;
+ }
+ final AttributeCache.Entry ent = attributeCache.get(packageName, theme,
+ R.styleable.Window, userId);
+ if (ent == null) {
+ return null;
+ }
+
+ final T style = mEntryFactory.apply(ent.array);
+ synchronized (mCache) {
+ if (themeMap == null) {
+ mCache.put(packageName, themeMap = new SparseArray<>());
+ }
+ themeMap.put(theme, style);
+ }
+ return style;
+ }
+
+ /** Called when the package is updated or removed. */
+ public void invalidatePackage(String packageName) {
+ synchronized (mCache) {
+ mCache.remove(packageName);
+ }
+ }
+}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index f07e6722d836..a8c49e11e4e9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -47,6 +47,7 @@
#include <dispatcher/Entry.h>
#include <include/gestures.h>
#include <input/Input.h>
+#include <input/InputFlags.h>
#include <input/PointerController.h>
#include <input/PrintTools.h>
#include <input/SpriteController.h>
@@ -124,6 +125,7 @@ static struct {
jmethodID notifyStylusGestureStarted;
jmethodID notifyVibratorState;
jmethodID filterInputEvent;
+ jmethodID filterPointerMotion;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptMotionBeforeQueueingNonInteractive;
jmethodID interceptKeyBeforeDispatching;
@@ -451,6 +453,8 @@ public:
void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
const vec2& position) override;
void notifyMouseCursorFadedOnTyping() override;
+ std::optional<vec2> filterPointerMotionForAccessibility(
+ const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) override;
/* --- InputFilterPolicyInterface implementation --- */
void notifyStickyModifierStateChanged(uint32_t modifierState,
@@ -663,7 +667,7 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
}
void NativeInputManager::setDisplayTopology(JNIEnv* env, jobject topologyGraph) {
- if (!com::android::input::flags::connected_displays_cursor()) {
+ if (!InputFlags::connectedDisplaysCursorEnabled()) {
return;
}
@@ -938,6 +942,27 @@ void NativeInputManager::notifyStickyModifierStateChanged(uint32_t modifierState
checkAndClearExceptionFromCallback(env, "notifyStickyModifierStateChanged");
}
+std::optional<vec2> NativeInputManager::filterPointerMotionForAccessibility(
+ const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
+ JNIEnv* env = jniEnv();
+ ScopedFloatArrayRO filtered(env,
+ jfloatArray(
+ env->CallObjectMethod(mServiceObj,
+ gServiceClassInfo.filterPointerMotion,
+ delta.x, delta.y, current.x,
+ current.y, displayId.val())));
+ if (checkAndClearExceptionFromCallback(env, "filterPointerMotionForAccessibilityLocked")) {
+ ALOGE("Disabling accessibility pointer motion filter due to an error. "
+ "The filter state in Java and PointerChoreographer would no longer be in sync.");
+ return std::nullopt;
+ }
+ LOG_ALWAYS_FATAL_IF(filtered.size() != 2,
+ "Accessibility pointer motion filter is misbehaving. Returned array size "
+ "%zu should be 2.",
+ filtered.size());
+ return vec2{filtered[0], filtered[1]};
+}
+
sp<SurfaceControl> NativeInputManager::getParentSurfaceForPointers(ui::LogicalDisplayId displayId) {
JNIEnv* env = jniEnv();
jlong nativeSurfaceControlPtr =
@@ -3271,6 +3296,12 @@ static jboolean nativeSetKernelWakeEnabled(JNIEnv* env, jobject nativeImplObj, j
return im->getInputManager()->getReader().setKernelWakeEnabled(deviceId, enabled);
}
+static void nativeSetAccessibilityPointerMotionFilterEnabled(JNIEnv* env, jobject nativeImplObj,
+ jboolean enabled) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ im->getInputManager()->getChoreographer().setAccessibilityPointerMotionFilterEnabled(enabled);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -3398,6 +3429,8 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"setInputMethodConnectionIsActive", "(Z)V", (void*)nativeSetInputMethodConnectionIsActive},
{"getLastUsedInputDeviceId", "()I", (void*)nativeGetLastUsedInputDeviceId},
{"setKernelWakeEnabled", "(IZ)Z", (void*)nativeSetKernelWakeEnabled},
+ {"setAccessibilityPointerMotionFilterEnabled", "(Z)V",
+ (void*)nativeSetAccessibilityPointerMotionFilterEnabled},
};
#define FIND_CLASS(var, className) \
@@ -3482,6 +3515,8 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
+ GET_METHOD_ID(gServiceClassInfo.filterPointerMotion, clazz, "filterPointerMotion", "(FFFFI)[F");
+
GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeQueueing, clazz,
"interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;I)I");
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index 40554ac51b24..7edf646a48f6 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -21,11 +21,16 @@ import static com.android.server.credentials.CredentialManagerService.getPrimary
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
+import android.credentials.flags.Flags;
import android.service.credentials.CredentialProviderInfoFactory;
+import android.service.credentials.CredentialProviderService;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -78,14 +83,16 @@ public final class CredentialManagerServiceImpl extends
mInfo = providerInfo;
}
- @Override // from PerUserSystemService when a new setting based service is to be created
+ @Override // from PerUserSystemService when a new service is to be created
@GuardedBy("mLock")
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
- throws PackageManager.NameNotFoundException {
+ throws PackageManager.NameNotFoundException, SecurityException, NullPointerException {
+ boolean isSystemProvider = false;
if (mInfo != null) {
Slog.i(TAG, "newServiceInfoLocked, mInfo not null : "
+ mInfo.getServiceInfo().getComponentName().flattenToString() + " , "
+ serviceComponent.flattenToString());
+ isSystemProvider = mInfo.isSystemProvider();
} else {
Slog.i(TAG, "newServiceInfoLocked, mInfo null, "
+ serviceComponent.flattenToString());
@@ -94,7 +101,7 @@ public final class CredentialManagerServiceImpl extends
getPrimaryProvidersForUserId(mMaster.getContext(), mUserId);
mInfo = CredentialProviderInfoFactory.create(
getContext(), serviceComponent,
- mUserId, /*isSystemProvider=*/false,
+ mUserId, isSystemProvider,
primaryProviders.contains(serviceComponent));
return mInfo.getServiceInfo();
}
@@ -148,15 +155,63 @@ public final class CredentialManagerServiceImpl extends
* @param packageName package of the app being updated.
*/
@GuardedBy("mLock")
+ @SuppressWarnings("GuardedBy") // ErrorProne requires this.mMaster.mLock which is the case
+ // because this method is called by this.mMaster anyway
protected void handlePackageUpdateLocked(@NonNull String packageName) {
if (mInfo != null && mInfo.getServiceInfo() != null
&& mInfo.getServiceInfo().getComponentName()
.getPackageName().equals(packageName)) {
- try {
- newServiceInfoLocked(mInfo.getServiceInfo().getComponentName());
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Issue while updating serviceInfo: " + e.getMessage());
+ if (Flags.packageUpdateFixEnabled()) {
+ try {
+ updateCredentialProviderInfo(mInfo.getServiceInfo().getComponentName(),
+ mInfo.isSystemProvider());
+ } catch (SecurityException | PackageManager.NameNotFoundException
+ | NullPointerException e) {
+ Slog.w(TAG, "Unable to update provider, must be removed: " + e.getMessage());
+ mMaster.handleServiceRemovedMultiModeLocked(mInfo.getComponentName(), mUserId);
+ }
+ } else {
+ try {
+ newServiceInfoLocked(mInfo.getServiceInfo().getComponentName());
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Issue while updating serviceInfo: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateCredentialProviderInfo(ComponentName componentName, boolean isSystemProvider)
+ throws SecurityException, PackageManager.NameNotFoundException {
+ Slog.d(TAG, "Updating credential provider: " + componentName.flattenToString());
+ if (!isValidCredentialProviderInfo(componentName, mUserId, isSystemProvider)) {
+ throw new SecurityException("Service has not been set up correctly");
+ }
+ newServiceInfoLocked(componentName);
+ }
+
+ private boolean isValidCredentialProviderInfo(ComponentName componentName, int userId,
+ boolean isSystemProvider) {
+ Context context = getContext();
+ if (context == null) {
+ return false;
+ }
+ String serviceInterface = CredentialProviderService.SERVICE_INTERFACE;
+ if (isSystemProvider) {
+ serviceInterface = CredentialProviderService.SYSTEM_SERVICE_INTERFACE;
+ }
+ final List<ResolveInfo> resolveInfos =
+ context.getPackageManager()
+ .queryIntentServicesAsUser(
+ new Intent(serviceInterface),
+ PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
+ userId);
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo.getComponentName().equals(componentName)) {
+ return true;
}
}
+ return false;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index bbbfe0b2f0a2..5b7e7f17c454 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -65,6 +65,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -120,11 +121,13 @@ final class DevicePolicyEngine {
/**
* Map of <userId, Map<policyKey, policyState>>
*/
+ @GuardedBy("mLock")
private final SparseArray<Map<PolicyKey, PolicyState<?>>> mLocalPolicies;
/**
* Map of <policyKey, policyState>
*/
+ @GuardedBy("mLock")
private final Map<PolicyKey, PolicyState<?>> mGlobalPolicies;
/**
@@ -152,6 +155,7 @@ final class DevicePolicyEngine {
mAdminPolicySize = new SparseArray<>();
}
+ @GuardedBy("mLock")
private void forceEnforcementRefreshIfUserRestrictionLocked(
@NonNull PolicyDefinition<?> policyDefinition) {
try {
@@ -185,6 +189,7 @@ final class DevicePolicyEngine {
return false;
}
+ @GuardedBy("mLock")
private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) {
Binder.withCleanCallingIdentity(() -> {
// Sync global state
@@ -296,6 +301,7 @@ final class DevicePolicyEngine {
*
* <p>Passing a {@code null} value means the policy set by this admin should be removed.
*/
+ @GuardedBy("mLock")
private <V> void setNonCoexistableLocalPolicyLocked(
PolicyDefinition<V> policyDefinition,
PolicyState<V> localPolicyState,
@@ -440,6 +446,7 @@ final class DevicePolicyEngine {
/**
* Enforces the new policy and notifies relevant admins.
*/
+ @GuardedBy("mLock")
private <V> void onLocalPolicyChangedLocked(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
@@ -600,6 +607,7 @@ final class DevicePolicyEngine {
/**
* Enforces the new policy globally and notifies relevant admins.
*/
+ @GuardedBy("mLock")
private <V> void onGlobalPolicyChangedLocked(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin) {
@@ -627,6 +635,7 @@ final class DevicePolicyEngine {
*
* <p>Returns {@code true} if the policy is enforced successfully on all users.
*/
+ @GuardedBy("mLock")
private <V> boolean applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
@@ -930,6 +939,7 @@ final class DevicePolicyEngine {
removePoliciesForAdmin(oldAdmin);
}
+ @GuardedBy("mLock")
private Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdminLocked(
Map<PolicyKey, PolicyState<?>> policies,
EnforcingAdmin admin) {
@@ -949,6 +959,7 @@ final class DevicePolicyEngine {
return keys;
}
+ @GuardedBy("mLock")
private <V> boolean hasLocalPolicyLocked(PolicyDefinition<V> policyDefinition, int userId) {
if (policyDefinition.isGlobalOnlyPolicy()) {
return false;
@@ -963,6 +974,7 @@ final class DevicePolicyEngine {
.getPoliciesSetByAdmins().isEmpty();
}
+ @GuardedBy("mLock")
private <V> boolean hasGlobalPolicyLocked(PolicyDefinition<V> policyDefinition) {
if (policyDefinition.isLocalOnlyPolicy()) {
return false;
@@ -974,6 +986,7 @@ final class DevicePolicyEngine {
.isEmpty();
}
+ @GuardedBy("mLock")
@NonNull
private <V> PolicyState<V> getLocalPolicyStateLocked(
PolicyDefinition<V> policyDefinition, int userId) {
@@ -993,6 +1006,7 @@ final class DevicePolicyEngine {
return getPolicyStateLocked(mLocalPolicies.get(userId), policyDefinition);
}
+ @GuardedBy("mLock")
private <V> void removeLocalPolicyStateLocked(
PolicyDefinition<V> policyDefinition, int userId) {
if (!mLocalPolicies.contains(userId)) {
@@ -1001,6 +1015,7 @@ final class DevicePolicyEngine {
mLocalPolicies.get(userId).remove(policyDefinition.getPolicyKey());
}
+ @GuardedBy("mLock")
@NonNull
private <V> PolicyState<V> getGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) {
if (policyDefinition.isLocalOnlyPolicy()) {
@@ -1015,10 +1030,12 @@ final class DevicePolicyEngine {
return getPolicyStateLocked(mGlobalPolicies, policyDefinition);
}
+ @GuardedBy("mLock")
private <V> void removeGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) {
mGlobalPolicies.remove(policyDefinition.getPolicyKey());
}
+ @GuardedBy("mLock")
private static <V> PolicyState<V> getPolicyStateLocked(
Map<PolicyKey, PolicyState<?>> policies, PolicyDefinition<V> policyDefinition) {
try {
@@ -1089,6 +1106,7 @@ final class DevicePolicyEngine {
}
// TODO(b/261430877): Finalise the decision on which admins to send the updates to.
+ @GuardedBy("mLock")
private <V> void sendPolicyChangedToAdminsLocked(
PolicyState<V> policyState,
EnforcingAdmin callingAdmin,
@@ -1378,6 +1396,7 @@ final class DevicePolicyEngine {
});
}
+ @GuardedBy("mLock")
private <V> void enforcePolicyOnUserLocked(int userId, PolicyState<V> policyState) {
if (!policyState.getPolicyDefinition().isInheritable()) {
return;
@@ -1509,6 +1528,7 @@ final class DevicePolicyEngine {
* Called after an admin policy has been added to start binding to the admin if a connection
* was not already established.
*/
+ @GuardedBy("mLock")
private void updateDeviceAdminServiceOnPolicyAddLocked(@NonNull EnforcingAdmin enforcingAdmin) {
int userId = enforcingAdmin.getUserId();
@@ -1537,6 +1557,7 @@ final class DevicePolicyEngine {
* Called after an admin policy has been removed to stop binding to the admin if they no longer
* have any policies set.
*/
+ @GuardedBy("mLock")
private void updateDeviceAdminServiceOnPolicyRemoveLocked(
@NonNull EnforcingAdmin enforcingAdmin) {
if (doesAdminHavePoliciesLocked(enforcingAdmin)) {
@@ -1562,6 +1583,7 @@ final class DevicePolicyEngine {
/* actionForLog= */ "policy-removed");
}
+ @GuardedBy("mLock")
private boolean doesAdminHavePoliciesLocked(@NonNull EnforcingAdmin enforcingAdmin) {
for (PolicyKey policy : mGlobalPolicies.keySet()) {
PolicyState<?> policyState = mGlobalPolicies.get(policy);
@@ -1785,6 +1807,7 @@ final class DevicePolicyEngine {
}
}
+ @GuardedBy("mLock")
<V> void reapplyAllPoliciesOnBootLocked() {
for (PolicyKey policy : mGlobalPolicies.keySet()) {
PolicyState<?> policyState = mGlobalPolicies.get(policy);
@@ -1919,6 +1942,7 @@ final class DevicePolicyEngine {
}
}
+ @GuardedBy("mLock")
void writeToFileLocked() {
Log.d(TAG, "Writing to " + mFile);
@@ -1931,7 +1955,7 @@ final class DevicePolicyEngine {
out.startDocument(null, true);
// Actual content
- writeInner(out);
+ writeInnerLocked(out);
out.endDocument();
out.flush();
@@ -1948,16 +1972,19 @@ final class DevicePolicyEngine {
}
}
+ @GuardedBy("mLock")
// TODO(b/256846294): Add versioning to read/write
- void writeInner(TypedXmlSerializer serializer) throws IOException {
- writeLocalPoliciesInner(serializer);
- writeGlobalPoliciesInner(serializer);
- writeEnforcingAdminsInner(serializer);
- writeEnforcingAdminSizeInner(serializer);
- writeMaxPolicySizeInner(serializer);
+ void writeInnerLocked(TypedXmlSerializer serializer) throws IOException {
+ writeLocalPoliciesInnerLocked(serializer);
+ writeGlobalPoliciesInnerLocked(serializer);
+ writeEnforcingAdminsInnerLocked(serializer);
+ writeEnforcingAdminSizeInnerLocked(serializer);
+ writeMaxPolicySizeInnerLocked(serializer);
}
- private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
+ @GuardedBy("mLock")
+ private void writeLocalPoliciesInnerLocked(TypedXmlSerializer serializer)
+ throws IOException {
if (mLocalPolicies != null) {
for (int i = 0; i < mLocalPolicies.size(); i++) {
int userId = mLocalPolicies.keyAt(i);
@@ -1981,7 +2008,9 @@ final class DevicePolicyEngine {
}
}
- private void writeGlobalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
+ @GuardedBy("mLock")
+ private void writeGlobalPoliciesInnerLocked(TypedXmlSerializer serializer)
+ throws IOException {
if (mGlobalPolicies != null) {
for (Map.Entry<PolicyKey, PolicyState<?>> policy : mGlobalPolicies.entrySet()) {
serializer.startTag(/* namespace= */ null, TAG_GLOBAL_POLICY_ENTRY);
@@ -1999,7 +2028,9 @@ final class DevicePolicyEngine {
}
}
- private void writeEnforcingAdminsInner(TypedXmlSerializer serializer) throws IOException {
+ @GuardedBy("mLock")
+ private void writeEnforcingAdminsInnerLocked(TypedXmlSerializer serializer)
+ throws IOException {
if (mEnforcingAdmins != null) {
for (int i = 0; i < mEnforcingAdmins.size(); i++) {
int userId = mEnforcingAdmins.keyAt(i);
@@ -2012,7 +2043,8 @@ final class DevicePolicyEngine {
}
}
- private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
+ @GuardedBy("mLock")
+ private void writeEnforcingAdminSizeInnerLocked(TypedXmlSerializer serializer)
throws IOException {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
@@ -2034,7 +2066,8 @@ final class DevicePolicyEngine {
}
}
- private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
+ @GuardedBy("mLock")
+ private void writeMaxPolicySizeInnerLocked(TypedXmlSerializer serializer)
throws IOException {
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
serializer.attributeInt(
@@ -2042,6 +2075,7 @@ final class DevicePolicyEngine {
serializer.endTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
}
+ @GuardedBy("mLock")
void readFromFileLocked() {
if (!mFile.exists()) {
Log.d(TAG, "" + mFile + " doesn't exist");
@@ -2055,7 +2089,7 @@ final class DevicePolicyEngine {
input = f.openRead();
TypedXmlPullParser parser = Xml.resolvePullParser(input);
- readInner(parser);
+ readInnerLocked(parser);
} catch (XmlPullParserException | IOException | ClassNotFoundException e) {
Slogf.wtf(TAG, "Error parsing resources file", e);
@@ -2064,7 +2098,8 @@ final class DevicePolicyEngine {
}
}
- private void readInner(TypedXmlPullParser parser)
+ @GuardedBy("mLock")
+ private void readInnerLocked(TypedXmlPullParser parser)
throws IOException, XmlPullParserException, ClassNotFoundException {
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 191c21e661d0..aee32a0473a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -423,6 +423,7 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.hardware.usb.UsbManager;
+import android.health.connect.HealthConnectManager;
import android.location.Location;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -2149,6 +2150,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
mBackgroundHandler = BackgroundThread.getHandler();
+ // Add the health permission to the list of restricted permissions.
+ if (android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) {
+ Set<String> healthPermissions = HealthConnectManager.getHealthPermissions(mContext);
+ for (String permission : healthPermissions) {
+ SENSOR_PERMISSIONS.add(permission);
+ }
+ }
+
// Needed when mHasFeature == false, because it controls the certificate warning text.
mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2bbd69c65eb8..e158310455ac 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1279,12 +1279,6 @@ public final class SystemServer implements Dumpable {
if (!Flags.refactorCrashrecovery()) {
// Initialize RescueParty.
CrashRecoveryAdaptor.rescuePartyRegisterHealthObserver(mSystemContext);
- if (!Flags.recoverabilityDetection()) {
- // Now that we have the bare essentials of the OS up and running, take
- // note that we just booted, which might send out a rescue party if
- // we're stuck in a runtime restart loop.
- CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
- }
}
@@ -1558,14 +1552,6 @@ public final class SystemServer implements Dumpable {
boolean enableVrService = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
- if (!Flags.recoverabilityDetection()) {
- // For debugging RescueParty
- if (Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.crash_system", false)) {
- throw new RuntimeException();
- }
- }
-
try {
final String SECONDARY_ZYGOTE_PRELOAD = "SecondaryZygotePreload";
// We start the preload ~1s before the webview factory preparation, to
@@ -3091,13 +3077,11 @@ public final class SystemServer implements Dumpable {
CrashRecoveryAdaptor.initializeCrashrecoveryModuleService(mSystemServiceManager);
t.traceEnd();
} else {
- if (Flags.recoverabilityDetection()) {
- // Now that we have the essential services needed for mitigations, register the boot
- // with package watchdog.
- // Note that we just booted, which might send out a rescue party if we're stuck in a
- // runtime restart loop.
- CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
- }
+ // Now that we have the essential services needed for mitigations, register the boot
+ // with package watchdog.
+ // Note that we just booted, which might send out a rescue party if we're stuck in a
+ // runtime restart loop.
+ CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext);
}
t.traceBegin("MakeDisplayManagerServiceReady");
@@ -3511,12 +3495,10 @@ public final class SystemServer implements Dumpable {
* are updated outside of OTA; and to avoid breaking dependencies from system into apexes.
*/
private void startApexServices(@NonNull TimingsTraceAndSlog t) {
- if (Flags.recoverabilityDetection()) {
- // For debugging RescueParty
- if (Build.IS_DEBUGGABLE
- && SystemProperties.getBoolean("debug.crash_system", false)) {
- throw new RuntimeException();
- }
+ // For debugging RescueParty
+ if (Build.IS_DEBUGGABLE
+ && SystemProperties.getBoolean("debug.crash_system", false)) {
+ throw new RuntimeException();
}
t.traceBegin("startApexServices");
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index a96c477c78d2..f731b50d81b4 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -17,6 +17,8 @@
package com.android.server.supervision;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_USERS;
+import static android.Manifest.permission.QUERY_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.Preconditions.checkCallAuthorization;
@@ -79,6 +81,25 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
/**
+ * Creates an {@link Intent} that can be used with {@link Context#startActivity(Intent)} to
+ * launch the activity to verify supervision credentials.
+ *
+ * <p>A valid {@link Intent} is always returned if supervision is enabled at the time this
+ * method is called, the launched activity still need to perform validity checks as the
+ * supervision state can change when it's launched. A null intent is returned if supervision is
+ * disabled at the time of this method call.
+ *
+ * <p>A result code of {@link android.app.Activity#RESULT_OK} indicates successful verification
+ * of the supervision credentials.
+ */
+ @Override
+ @Nullable
+ public Intent createConfirmSupervisionCredentialsIntent() {
+ // TODO(b/392961554): Implement createAuthenticationIntent API
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Returns whether supervision is enabled for the given user.
*
* <p>Supervision is automatically enabled when the supervision app becomes the profile owner or
@@ -86,6 +107,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
*/
@Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
+ enforceAnyPermission(QUERY_USERS, MANAGE_USERS);
if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
enforcePermission(INTERACT_ACROSS_USERS);
}
@@ -96,6 +118,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Override
public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+ // TODO(b/395630828): Ensure that this method can only be called by the system.
if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
enforcePermission(INTERACT_ACROSS_USERS);
}
@@ -181,8 +204,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
* Ensures that supervision is enabled when the supervision app is the profile owner.
*
* <p>The state syncing with the DevicePolicyManager can only enable supervision and never
- * disable. Supervision can only be disabled explicitly via calls to the
- * {@link #setSupervisionEnabledForUser} method.
+ * disable. Supervision can only be disabled explicitly via calls to the {@link
+ * #setSupervisionEnabledForUser} method.
*/
private void syncStateWithDevicePolicyManager(@UserIdInt int userId) {
final DevicePolicyManagerInternal dpmInternal = mInjector.getDpmInternal();
@@ -221,6 +244,17 @@ public class SupervisionService extends ISupervisionManager.Stub {
mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED);
}
+ /** Enforces that the caller has at least one of the given permission. */
+ private void enforceAnyPermission(String... permissions) {
+ boolean authorized = false;
+ for (String permission : permissions) {
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ authorized = true;
+ }
+ }
+ checkCallAuthorization(authorized);
+ }
+
/** Provides local services in a lazy manner. */
static class Injector {
private final Context mContext;
@@ -280,7 +314,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
@VisibleForTesting
- @SuppressLint("MissingPermission") // not needed for a system service
+ @SuppressLint("MissingPermission")
void registerProfileOwnerListener() {
IntentFilter poIntentFilter = new IntentFilter();
poIntentFilter.addAction(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 5d64cb638702..ae5e85163e9a 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -34,12 +34,12 @@ import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.res.Configuration;
import android.graphics.Insets;
+import android.os.Build;
import android.os.RemoteException;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.server.wm.WindowManagerStateHelper;
@@ -86,25 +86,36 @@ public class InputMethodServiceTest {
"android:id/input_method_nav_back";
private static final String INPUT_METHOD_NAV_IME_SWITCHER_ID =
"android:id/input_method_nav_ime_switcher";
- private static final long TIMEOUT_IN_SECONDS = 3;
- private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
- "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 1";
- private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
- "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 0";
+
+ /** Timeout until the uiObject should be found. */
+ private static final long TIMEOUT_MS = 5000L * Build.HW_TIMEOUT_MULTIPLIER;
+
+ /** Timeout until the event is expected. */
+ private static final long EXPECT_TIMEOUT_MS = 3000L * Build.HW_TIMEOUT_MULTIPLIER;
+
+ /** Timeout during which the event is not expected. */
+ private static final long NOT_EXCEPT_TIMEOUT_MS = 2000L * Build.HW_TIMEOUT_MULTIPLIER;
+
+ /** Command to set showing the IME when a hardware keyboard is connected. */
+ private static final String SET_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
+ "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD;
+ /** Command to get verbose ImeTracker logging state. */
+ private static final String GET_VERBOSE_IME_TRACKER_LOGGING_CMD =
+ "getprop persist.debug.imetracker";
+ /** Command to set verbose ImeTracker logging state. */
+ private static final String SET_VERBOSE_IME_TRACKER_LOGGING_CMD =
+ "setprop persist.debug.imetracker";
/** The ids of the subtypes of SimpleIme. */
private static final int[] SUBTYPE_IDS = new int[]{1, 2};
- private final WindowManagerStateHelper mWmState = new WindowManagerStateHelper();
+ private final WindowManagerStateHelper mWmState = new WindowManagerStateHelper();
private final GestureNavSwitchHelper mGestureNavSwitchHelper = new GestureNavSwitchHelper();
private final DeviceFlagsValueProvider mFlagsValueProvider = new DeviceFlagsValueProvider();
@Rule
- public final CheckFlagsRule mCheckFlagsRule = new CheckFlagsRule(mFlagsValueProvider);
-
- @Rule
public final TestName mName = new TestName();
private Instrumentation mInstrumentation;
@@ -114,7 +125,8 @@ public class InputMethodServiceTest {
private String mInputMethodId;
private TestActivity mActivity;
private InputMethodServiceWrapper mInputMethodService;
- private boolean mShowImeWithHardKeyboardEnabled;
+ private boolean mOriginalVerboseImeTrackerLoggingEnabled;
+ private boolean mOriginalShowImeWithHardKeyboardEnabled;
@Before
public void setUp() throws Exception {
@@ -123,9 +135,12 @@ public class InputMethodServiceTest {
mImm = mInstrumentation.getContext().getSystemService(InputMethodManager.class);
mTargetPackageName = mInstrumentation.getTargetContext().getPackageName();
mInputMethodId = getInputMethodId();
+ mOriginalVerboseImeTrackerLoggingEnabled = getVerboseImeTrackerLogging();
+ if (!mOriginalVerboseImeTrackerLoggingEnabled) {
+ setVerboseImeTrackerLogging(true);
+ }
prepareIme();
prepareActivity();
- mInstrumentation.waitForIdleSync();
mUiDevice.freezeRotation();
mUiDevice.setOrientationNatural();
// Waits for input binding ready.
@@ -148,17 +163,18 @@ public class InputMethodServiceTest {
.that(mInputMethodService.getCurrentInputViewStarted()).isFalse();
});
// Save the original value of show_ime_with_hard_keyboard from Settings.
- mShowImeWithHardKeyboardEnabled =
+ mOriginalShowImeWithHardKeyboardEnabled =
mInputMethodService.getShouldShowImeWithHardKeyboardForTesting();
}
@After
public void tearDown() throws Exception {
mUiDevice.unfreezeRotation();
+ if (!mOriginalVerboseImeTrackerLoggingEnabled) {
+ setVerboseImeTrackerLogging(false);
+ }
// Change back the original value of show_ime_with_hard_keyboard in Settings.
- executeShellCommand(mShowImeWithHardKeyboardEnabled
- ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
- : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
+ setShowImeWithHardKeyboard(mOriginalShowImeWithHardKeyboardEnabled);
executeShellCommand("ime disable " + mInputMethodId);
}
@@ -170,7 +186,7 @@ public class InputMethodServiceTest {
public void testShowHideKeyboard_byUserAction() {
waitUntilActivityReadyForInputInjection(mActivity);
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
// Performs click on EditText to bring up the IME.
Log.i(TAG, "Click on EditText");
@@ -201,14 +217,12 @@ public class InputMethodServiceTest {
*/
@Test
public void testShowHideKeyboard_byInputMethodManager() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
- // Triggers to show IME via public API.
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
- // Triggers to hide IME via public API.
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(),
EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
@@ -219,14 +233,12 @@ public class InputMethodServiceTest {
*/
@Test
public void testShowHideKeyboard_byInsetsController() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
- // Triggers to show IME via public API.
verifyInputViewStatusOnMainSync(
() -> mActivity.showImeWithWindowInsetsController(),
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
- // Triggers to hide IME via public API.
verifyInputViewStatusOnMainSync(
() -> mActivity.hideImeWithWindowInsetsController(),
EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
@@ -234,53 +246,18 @@ public class InputMethodServiceTest {
/**
* This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
- *
- * <p>With the refactor in b/298172246, all calls to IMMS#{show,hide}MySoftInputLocked
- * will be just apply the requested visibility (by using the callback). Therefore, we will
- * lose flags like HIDE_IMPLICIT_ONLY.
*/
@Test
public void testShowHideSelf() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
- // IME request to show itself without any flags, expect shown.
- Log.i(TAG, "Call IMS#requestShowSelf(0)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestShowSelf(0 /* flags */),
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
- if (!mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
- // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect not hide (shown).
- Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
- verifyInputViewStatusOnMainSync(
- () -> mInputMethodService.requestHideSelf(
- InputMethodManager.HIDE_IMPLICIT_ONLY),
- EVENT_HIDE, false /* eventExpected */, true /* shown */,
- "IME is still shown after HIDE_IMPLICIT_ONLY");
- }
-
- // IME request to hide itself without any flags, expect hidden.
- Log.i(TAG, "Call IMS#requestHideSelf(0)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestHideSelf(0 /* flags */),
EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
-
- if (!mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
- // IME request to show itself with flag SHOW_IMPLICIT, expect shown.
- Log.i(TAG, "Call IMS#requestShowSelf(InputMethodManager.SHOW_IMPLICIT)");
- verifyInputViewStatusOnMainSync(
- () -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT),
- EVENT_SHOW, true /* eventExpected */, true /* shown */,
- "IME is shown with SHOW_IMPLICIT");
-
- // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect hidden.
- Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
- verifyInputViewStatusOnMainSync(
- () -> mInputMethodService.requestHideSelf(
- InputMethodManager.HIDE_IMPLICIT_ONLY),
- EVENT_HIDE, true /* eventExpected */, false /* shown */,
- "IME is not shown after HIDE_IMPLICIT_ONLY");
- }
}
/**
@@ -289,28 +266,25 @@ public class InputMethodServiceTest {
*/
@Test
public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
try {
config.keyboard = Configuration.KEYBOARD_QWERTY;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
- eventually(() ->
- assertWithMessage("InputView should show with visible hardware keyboard")
- .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+ assertWithMessage("InputView should show with visible hardware keyboard")
+ .that(mInputMethodService.onEvaluateInputViewShown()).isTrue();
config.keyboard = Configuration.KEYBOARD_NOKEYS;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
- eventually(() ->
- assertWithMessage("InputView should show without hardware keyboard")
- .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+ assertWithMessage("InputView should show without hardware keyboard")
+ .that(mInputMethodService.onEvaluateInputViewShown()).isTrue();
config.keyboard = Configuration.KEYBOARD_QWERTY;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
- eventually(() ->
- assertWithMessage("InputView should show with hidden hardware keyboard")
- .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+ assertWithMessage("InputView should show with hidden hardware keyboard")
+ .that(mInputMethodService.onEvaluateInputViewShown()).isTrue();
} finally {
mInputMethodService.getResources()
.updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
@@ -323,28 +297,25 @@ public class InputMethodServiceTest {
*/
@Test
public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
try {
config.keyboard = Configuration.KEYBOARD_QWERTY;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
- eventually(() ->
- assertWithMessage("InputView should not show with visible hardware keyboard")
- .that(mInputMethodService.onEvaluateInputViewShown()).isFalse());
+ assertWithMessage("InputView should not show with visible hardware keyboard")
+ .that(mInputMethodService.onEvaluateInputViewShown()).isFalse();
config.keyboard = Configuration.KEYBOARD_NOKEYS;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
- eventually(() ->
- assertWithMessage("InputView should show without hardware keyboard")
- .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+ assertWithMessage("InputView should show without hardware keyboard")
+ .that(mInputMethodService.onEvaluateInputViewShown()).isTrue();
config.keyboard = Configuration.KEYBOARD_QWERTY;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
- eventually(() ->
- assertWithMessage("InputView should show with hidden hardware keyboard")
- .that(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+ assertWithMessage("InputView should show with hidden hardware keyboard")
+ .that(mInputMethodService.onEvaluateInputViewShown()).isTrue();
} finally {
mInputMethodService.getResources()
.updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
@@ -357,7 +328,7 @@ public class InputMethodServiceTest {
*/
@Test
public void testShowSoftInput_disableShowImeWithHardKeyboard() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
@@ -386,49 +357,17 @@ public class InputMethodServiceTest {
}
/**
- * This checks that an explicit show request results in the IME being shown.
- */
- @Test
- public void testShowSoftInputExplicitly() {
- setShowImeWithHardKeyboard(true /* enabled */);
-
- // When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the
- // IME should be shown.
- verifyInputViewStatusOnMainSync(
- () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
- EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
- }
-
- /**
- * This checks that an implicit show request results in the IME being shown.
- */
- @Test
- public void testShowSoftInputImplicitly() {
- setShowImeWithHardKeyboard(true /* enabled */);
-
- // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT,
- // the IME should be shown.
- verifyInputViewStatusOnMainSync(() -> assertThat(
- mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
- EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
- }
-
- /**
* This checks that an explicit show request when the IME is not previously shown,
* and it should be shown in fullscreen mode, results in the IME being shown.
*/
@Test
public void testShowSoftInputExplicitly_fullScreenMode() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
// Set orientation landscape to enable fullscreen mode.
setOrientation(2);
- eventually(() -> assertWithMessage("No longer in natural orientation")
- .that(mUiDevice.isNaturalOrientation()).isFalse());
- // Wait for the TestActivity to be recreated.
eventually(() -> assertWithMessage("Activity was re-created after rotation")
.that(TestActivity.getInstance()).isNotEqualTo(mActivity));
- // Get the new TestActivity.
mActivity = TestActivity.getInstance();
assertWithMessage("Re-created activity is not null").that(mActivity).isNotNull();
// Wait for the new EditText to be served by InputMethodManager.
@@ -442,34 +381,40 @@ public class InputMethodServiceTest {
/**
* This checks that an implicit show request when the IME is not previously shown,
- * and it should be shown in fullscreen mode, results in the IME not being shown.
+ * and it should be shown in fullscreen mode behaves like an explicit show request, resulting
+ * in the IME being shown. This is due to the refactor in b/298172246, causing us to lose flag
+ * information like {@link InputMethodManager#SHOW_IMPLICIT}.
*
- * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
- * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
- * SHOW_IMPLICIT.
+ * <p>Previously, an implicit show request when the IME is not previously shown,
+ * and it should be shown in fullscreen mode, would result in the IME not being shown.
*/
@Test
- @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputImplicitly_fullScreenMode() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
// Set orientation landscape to enable fullscreen mode.
setOrientation(2);
- eventually(() -> assertWithMessage("No longer in natural orientation")
- .that(mUiDevice.isNaturalOrientation()).isFalse());
- // Wait for the TestActivity to be recreated.
eventually(() -> assertWithMessage("Activity was re-created after rotation")
.that(TestActivity.getInstance()).isNotEqualTo(mActivity));
- // Get the new TestActivity.
mActivity = TestActivity.getInstance();
assertWithMessage("Re-created activity is not null").that(mActivity).isNotNull();
// Wait for the new EditText to be served by InputMethodManager.
eventually(() -> assertWithMessage("Has an input connection to the re-created Activity")
.that(mImm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
- verifyInputViewStatusOnMainSync(() -> assertThat(
- mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
- EVENT_SHOW, false /* eventExpected */, false /* shown */, "IME is not shown");
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(
+ InputMethodManager.SHOW_IMPLICIT))
+ .isTrue(),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ } else {
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(
+ InputMethodManager.SHOW_IMPLICIT))
+ .isTrue(),
+ EVENT_SHOW, false /* eventExpected */, false /* shown */, "IME is not shown");
+ }
}
/**
@@ -478,7 +423,7 @@ public class InputMethodServiceTest {
*/
@Test
public void testShowSoftInputExplicitly_withHardKeyboard() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
@@ -497,17 +442,17 @@ public class InputMethodServiceTest {
}
/**
- * This checks that an implicit show request when a hardware keyboard is connected,
- * results in the IME not being shown.
+ * This checks that an implicit show request when a hardware keyboard is connected behaves
+ * like an explicit show request, resulting in the IME being shown. This is due to the
+ * refactor in b/298172246, causing us to lose flag information like
+ * {@link InputMethodManager#SHOW_IMPLICIT}.
*
- * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
- * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
- * SHOW_IMPLICIT.
+ * <p>Previously, an implicit show request when a hardware keyboard is connected would
+ * result in the IME not being shown.
*/
@Test
- @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputImplicitly_withHardKeyboard() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
@@ -516,10 +461,20 @@ public class InputMethodServiceTest {
config.keyboard = Configuration.KEYBOARD_QWERTY;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
- verifyInputViewStatusOnMainSync(() ->assertThat(
- mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT))
- .isTrue(),
- EVENT_SHOW, false /* eventExpected */, false /* shown */, "IME is not shown");
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(
+ InputMethodManager.SHOW_IMPLICIT))
+ .isTrue(),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ } else {
+ verifyInputViewStatusOnMainSync(() -> assertThat(
+ mActivity.showImeWithInputMethodManager(
+ InputMethodManager.SHOW_IMPLICIT))
+ .isTrue(),
+ EVENT_SHOW, false /* eventExpected */, false /* shown */,
+ "IME is not shown");
+ }
} finally {
mInputMethodService.getResources()
.updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
@@ -532,7 +487,7 @@ public class InputMethodServiceTest {
*/
@Test
public void testShowSoftInputExplicitly_thenConfigurationChanged() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
@@ -565,17 +520,17 @@ public class InputMethodServiceTest {
/**
* This checks that an implicit show request followed by connecting a hardware keyboard
- * and a configuration change, does not trigger IMS#onFinishInputView,
- * but results in the IME being hidden.
+ * and a configuration change behaves like an explicit show request, resulting in the IME
+ * still being shown. This is due to the refactor in b/298172246, causing us to lose flag
+ * information like {@link InputMethodManager#SHOW_IMPLICIT}.
*
- * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
- * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
- * SHOW_IMPLICIT.
+ * <p>Previously, an implicit show request followed by connecting a hardware keyboard
+ * and a configuration change, would not trigger IMS#onFinishInputView, but resulted in the
+ * IME being hidden.
*/
@Test
- @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputImplicitly_thenConfigurationChanged() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
@@ -596,16 +551,23 @@ public class InputMethodServiceTest {
// Simulate a fake configuration change to avoid the recreation of TestActivity.
config.orientation = Configuration.ORIENTATION_LANDSCAPE;
- // Normally, IMS#onFinishInputView will be called when finishing the input view by
- // the user. But if IMS#hideWindow is called when receiving a new configuration change,
- // we don't expect that it's user-driven to finish the lifecycle of input view with
- // IMS#onFinishInputView, because the input view will be re-initialized according
- // to the last #mShowInputRequested state. So in this case we treat the input view as
- // still alive.
- verifyInputViewStatusOnMainSync(
- () -> mInputMethodService.onConfigurationChanged(config),
- EVENT_CONFIG, true /* eventExpected */, true /* inputViewStarted */,
- false /* shown */, "IME is not shown after a configuration change");
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.onConfigurationChanged(config),
+ EVENT_CONFIG, true /* eventExpected */, true /* shown */,
+ "IME is still shown after a configuration change");
+ } else {
+ // Normally, IMS#onFinishInputView will be called when finishing the input view by
+ // the user. But if IMS#hideWindow is called when receiving a new configuration
+ // change, we don't expect that it's user-driven to finish the lifecycle of input
+ // view with IMS#onFinishInputView, because the input view will be re-initialized
+ // according to the last #mShowInputRequested state. So in this case we treat the
+ // input view as still alive.
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.onConfigurationChanged(config),
+ EVENT_CONFIG, true /* eventExpected */, true /* inputViewStarted */,
+ false /* shown */, "IME is not shown after a configuration change");
+ }
} finally {
mInputMethodService.getResources()
.updateConfiguration(initialConfig, null /* metrics */, null /* compat */);
@@ -619,7 +581,7 @@ public class InputMethodServiceTest {
*/
@Test
public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard() {
- setShowImeWithHardKeyboard(false /* enabled */);
+ setShowImeWithHardKeyboard(false /* enable */);
final var config = mInputMethodService.getResources().getConfiguration();
final var initialConfig = new Configuration(config);
@@ -628,12 +590,10 @@ public class InputMethodServiceTest {
config.keyboard = Configuration.KEYBOARD_QWERTY;
config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
- // Explicit show request.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
- // Implicit show request.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(
InputMethodManager.SHOW_IMPLICIT)).isTrue(),
@@ -654,17 +614,18 @@ public class InputMethodServiceTest {
/**
* This checks that a forced show request directly followed by an explicit show request,
- * and then a hide not always request, still results in the IME being shown
- * (i.e. the explicit show request retains the forced state).
+ * and then a not always hide request behaves like a normal hide request, resulting in the
+ * IME being hidden (i.e. the explicit show request does not retain the forced state). This is
+ * due to the refactor in b/298172246, causing us to lose flag information like
+ * {@link InputMethodManager#SHOW_FORCED}.
*
- * <p>With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
- * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
- * HIDE_NOT_ALWAYS.
+ * <p>Previously, a forced show request directly followed by an explicit show request,
+ * and then a not always hide request, would result in the IME still being shown
+ * (i.e. the explicit show request would retain the forced state).
*/
@Test
- @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_FORCED)).isTrue(),
@@ -674,11 +635,123 @@ public class InputMethodServiceTest {
mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
EVENT_SHOW, false /* eventExpected */, true /* shown */, "IME is still shown");
- verifyInputViewStatusOnMainSync(() -> assertThat(
- mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS))
- .isTrue(),
- EVENT_HIDE, false /* eventExpected */, true /* shown */,
- "IME is still shown after HIDE_NOT_ALWAYS");
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ verifyInputViewStatusOnMainSync(() -> assertThat(mActivity
+ .hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS))
+ .isTrue(),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */,
+ "IME is not shown after HIDE_NOT_ALWAYS");
+ } else {
+ verifyInputViewStatusOnMainSync(() -> assertThat(mActivity
+ .hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS))
+ .isTrue(),
+ EVENT_HIDE, false /* eventExpected */, true /* shown */,
+ "IME is still shown after HIDE_NOT_ALWAYS");
+ }
+ }
+
+ /**
+ * This checks that an explicit show request followed by an implicit only hide request
+ * behaves like a normal hide request, resulting in the IME being hidden. This is due to
+ * the refactor in b/298172246, causing us to lose flag information like
+ * {@link InputMethodManager#SHOW_IMPLICIT} and {@link InputMethodManager#HIDE_IMPLICIT_ONLY}.
+ *
+ * <p>Previously, an explicit show request followed by an implicit only hide request
+ * would result in the IME still being shown.
+ */
+ @Test
+ public void testShowSoftInputExplicitly_thenHideSoftInputImplicitOnly() {
+ setShowImeWithHardKeyboard(true /* enable */);
+
+ verifyInputViewStatusOnMainSync(
+ () -> mActivity.showImeWithInputMethodManager(0 /* flags */),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ verifyInputViewStatusOnMainSync(
+ () -> mActivity.hideImeWithInputMethodManager(
+ InputMethodManager.HIDE_IMPLICIT_ONLY),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */,
+ "IME is not shown after HIDE_IMPLICIT_ONLY");
+ } else {
+ verifyInputViewStatusOnMainSync(
+ () -> mActivity.hideImeWithInputMethodManager(
+ InputMethodManager.HIDE_IMPLICIT_ONLY),
+ EVENT_HIDE, false /* eventExpected */, true /* shown */,
+ "IME is still shown after HIDE_IMPLICIT_ONLY");
+ }
+ }
+
+ /**
+ * This checks that an implicit show request followed by an implicit only hide request
+ * results in the IME being hidden.
+ */
+ @Test
+ public void testShowSoftInputImplicitly_thenHideSoftInputImplicitOnly() {
+ setShowImeWithHardKeyboard(true /* enable */);
+
+ verifyInputViewStatusOnMainSync(
+ () -> mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */,
+ "IME is shown with SHOW_IMPLICIT");
+
+ verifyInputViewStatusOnMainSync(
+ () -> mActivity.hideImeWithInputMethodManager(
+ InputMethodManager.HIDE_IMPLICIT_ONLY),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */,
+ "IME is not shown after HIDE_IMPLICIT_ONLY");
+ }
+
+ /**
+ * This checks that an explicit show self request followed by an implicit only hide self request
+ * behaves like a normal hide self request, resulting in the IME being hidden. This is due to
+ * the refactor in b/298172246, causing us to lose flag information like
+ * {@link InputMethodManager#SHOW_IMPLICIT} and {@link InputMethodManager#HIDE_IMPLICIT_ONLY}.
+ *
+ * <p>Previously, an explicit show self request followed by an implicit only hide self request
+ * would result in the IME still being shown.
+ */
+ @Test
+ public void testShowSelfExplicitly_thenHideSelfImplicitOnly() {
+ setShowImeWithHardKeyboard(true /* enable */);
+
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.requestShowSelf(0 /* flags */),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+
+ if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) {
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.requestHideSelf(
+ InputMethodManager.HIDE_IMPLICIT_ONLY),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */,
+ "IME is not shown after HIDE_IMPLICIT_ONLY");
+ } else {
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.requestHideSelf(
+ InputMethodManager.HIDE_IMPLICIT_ONLY),
+ EVENT_HIDE, false /* eventExpected */, true /* shown */,
+ "IME is still shown after HIDE_IMPLICIT_ONLY");
+ }
+ }
+
+ /**
+ * This checks that an implicit show self request followed by an implicit only hide self request
+ * results in the IME being hidden.
+ */
+ @Test
+ public void testShowSelfImplicitly_thenHideSelfImplicitOnly() {
+ setShowImeWithHardKeyboard(true /* enable */);
+
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */,
+ "IME is shown with SHOW_IMPLICIT");
+
+ verifyInputViewStatusOnMainSync(
+ () -> mInputMethodService.requestHideSelf(
+ InputMethodManager.HIDE_IMPLICIT_ONLY),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */,
+ "IME is not shown after HIDE_IMPLICIT_ONLY");
}
/**
@@ -686,7 +759,7 @@ public class InputMethodServiceTest {
*/
@Test
public void testFullScreenMode() {
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
Log.i(TAG, "Set orientation natural");
verifyFullscreenMode(() -> setOrientation(0), false /* eventExpected */,
@@ -723,25 +796,22 @@ public class InputMethodServiceTest {
public void testShowHideImeNavigationBar_doesDrawImeNavBar() {
assumeTrue("Must have a navigation bar", hasNavigationBar());
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
- // Show IME
verifyInputViewStatusOnMainSync(
() -> {
- setDrawsImeNavBarAndSwitcherButton(true /* enabled */);
+ setDrawsImeNavBarAndSwitcherButton(true /* enable */);
mActivity.showImeWithWindowInsetsController();
},
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
assertWithMessage("IME navigation bar is initially shown")
.that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue();
- // Try to hide IME nav bar
mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(false /* show */));
mInstrumentation.waitForIdleSync();
assertWithMessage("IME navigation bar is not shown after hide request")
.that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
- // Try to show IME nav bar
mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(true /* show */));
mInstrumentation.waitForIdleSync();
assertWithMessage("IME navigation bar is shown after show request")
@@ -758,25 +828,22 @@ public class InputMethodServiceTest {
public void testShowHideImeNavigationBar_doesNotDrawImeNavBar() {
assumeTrue("Must have a navigation bar", hasNavigationBar());
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
- // Show IME
verifyInputViewStatusOnMainSync(
() -> {
- setDrawsImeNavBarAndSwitcherButton(false /* enabled */);
+ setDrawsImeNavBarAndSwitcherButton(false /* enable */);
mActivity.showImeWithWindowInsetsController();
},
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
assertWithMessage("IME navigation bar is initially not shown")
.that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
- // Try to hide IME nav bar
mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(false /* show */));
mInstrumentation.waitForIdleSync();
assertWithMessage("IME navigation bar is not shown after hide request")
.that(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse();
- // Try to show IME nav bar
mInstrumentation.runOnMainSync(() -> setShowImeNavigationBar(true /* show */));
mInstrumentation.waitForIdleSync();
assertWithMessage("IME navigation bar is not shown after show request")
@@ -792,13 +859,16 @@ public class InputMethodServiceTest {
waitUntilActivityReadyForInputInjection(mActivity);
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
try (var ignored = mGestureNavSwitchHelper.withGestureNavigationMode()) {
verifyInputViewStatusOnMainSync(
() -> mActivity.showImeWithWindowInsetsController(),
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ eventually(() -> assertWithMessage("IME navigation bar is shown")
+ .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue());
+
final var backButton = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID));
verifyInputViewStatus(
() -> {
@@ -818,13 +888,16 @@ public class InputMethodServiceTest {
waitUntilActivityReadyForInputInjection(mActivity);
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
try (var ignored = mGestureNavSwitchHelper.withGestureNavigationMode()) {
verifyInputViewStatusOnMainSync(
() -> mActivity.showImeWithWindowInsetsController(),
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ eventually(() -> assertWithMessage("IME navigation bar is shown")
+ .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue());
+
final var backButton = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID));
verifyInputViewStatus(
() -> {
@@ -844,7 +917,7 @@ public class InputMethodServiceTest {
waitUntilActivityReadyForInputInjection(mActivity);
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
final var info = mImm.getCurrentInputMethodInfo();
assertWithMessage("InputMethodInfo is not null").that(info).isNotNull();
@@ -855,11 +928,14 @@ public class InputMethodServiceTest {
try (var ignored = mGestureNavSwitchHelper.withGestureNavigationMode()) {
verifyInputViewStatusOnMainSync(
() -> {
- setDrawsImeNavBarAndSwitcherButton(true /* enabled */);
+ setDrawsImeNavBarAndSwitcherButton(true /* enable */);
mActivity.showImeWithWindowInsetsController();
},
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ eventually(() -> assertWithMessage("IME navigation bar is shown")
+ .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue());
+
final var imeSwitcherButton = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID));
imeSwitcherButton.click();
mInstrumentation.waitForIdleSync();
@@ -884,7 +960,7 @@ public class InputMethodServiceTest {
waitUntilActivityReadyForInputInjection(mActivity);
- setShowImeWithHardKeyboard(true /* enabled */);
+ setShowImeWithHardKeyboard(true /* enable */);
final var info = mImm.getCurrentInputMethodInfo();
assertWithMessage("InputMethodInfo is not null").that(info).isNotNull();
@@ -893,11 +969,14 @@ public class InputMethodServiceTest {
try (var ignored = mGestureNavSwitchHelper.withGestureNavigationMode()) {
verifyInputViewStatusOnMainSync(
() -> {
- setDrawsImeNavBarAndSwitcherButton(true /* enabled */);
+ setDrawsImeNavBarAndSwitcherButton(true /* enable */);
mActivity.showImeWithWindowInsetsController();
},
EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ eventually(() -> assertWithMessage("IME navigation bar is shown")
+ .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue());
+
final var imeSwitcherButton = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID));
imeSwitcherButton.longClick();
mInstrumentation.waitForIdleSync();
@@ -956,7 +1035,8 @@ public class InputMethodServiceTest {
runnable.run();
}
mInstrumentation.waitForIdleSync();
- eventCalled = latch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ eventCalled = latch.await(eventExpected ? EXPECT_TIMEOUT_MS : NOT_EXCEPT_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("Interrupted while waiting for latch: " + e.getMessage());
return;
@@ -1016,10 +1096,8 @@ public class InputMethodServiceTest {
verifyInputViewStatus(runnable, EVENT_CONFIG, eventExpected, false /* shown */,
"IME is not shown");
if (eventExpected) {
- // Wait for the TestActivity to be recreated.
eventually(() -> assertWithMessage("Activity was re-created after rotation")
.that(TestActivity.getInstance()).isNotEqualTo(mActivity));
- // Get the new TestActivity.
mActivity = TestActivity.getInstance();
assertWithMessage("Re-created activity is not null").that(mActivity).isNotNull();
// Wait for the new EditText to be served by InputMethodManager.
@@ -1062,6 +1140,7 @@ public class InputMethodServiceTest {
private void prepareActivity() {
mActivity = TestActivity.startSync(mInstrumentation);
+ mInstrumentation.waitForIdleSync();
Log.i(TAG, "Finish preparing activity with editor.");
}
@@ -1086,21 +1165,51 @@ public class InputMethodServiceTest {
* @param enable the value to be set.
*/
private void setShowImeWithHardKeyboard(boolean enable) {
+ if (mInputMethodService == null) {
+ // If the IME is no longer around, reset the setting unconditionally.
+ executeShellCommand(SET_SHOW_IME_WITH_HARD_KEYBOARD_CMD + " " + (enable ? "1" : "0"));
+ return;
+ }
+
final boolean currentEnabled =
mInputMethodService.getShouldShowImeWithHardKeyboardForTesting();
if (currentEnabled != enable) {
- executeShellCommand(enable
- ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
- : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
+ executeShellCommand(SET_SHOW_IME_WITH_HARD_KEYBOARD_CMD + " " + (enable ? "1" : "0"));
eventually(() -> assertWithMessage("showImeWithHardKeyboard updated")
.that(mInputMethodService.getShouldShowImeWithHardKeyboardForTesting())
.isEqualTo(enable));
}
}
- private static void executeShellCommand(@NonNull String cmd) {
+ /**
+ * Gets the verbose logging state in {@link android.view.inputmethod.ImeTracker}.
+ *
+ * @return {@code true} iff verbose logging is enabled.
+ */
+ private static boolean getVerboseImeTrackerLogging() {
+ return executeShellCommand(GET_VERBOSE_IME_TRACKER_LOGGING_CMD).trim().equals("1");
+ }
+
+ /**
+ * Sets verbose logging in {@link android.view.inputmethod.ImeTracker}.
+ *
+ * @param enabled whether to enable or disable verbose logging.
+ *
+ * @implNote This must use {@link ActivityManager#notifySystemPropertiesChanged()} to listen
+ * for changes to the system property for the verbose ImeTracker logging.
+ */
+ private void setVerboseImeTrackerLogging(boolean enabled) {
+ final var context = mInstrumentation.getContext();
+ final var am = context.getSystemService(ActivityManager.class);
+
+ executeShellCommand(SET_VERBOSE_IME_TRACKER_LOGGING_CMD + " " + (enabled ? "1" : "0"));
+ am.notifySystemPropertiesChanged();
+ }
+
+ @NonNull
+ private static String executeShellCommand(@NonNull String cmd) {
Log.i(TAG, "Run command: " + cmd);
- SystemUtil.runShellCommandOrThrow(cmd);
+ return SystemUtil.runShellCommandOrThrow(cmd);
}
/**
@@ -1113,8 +1222,7 @@ public class InputMethodServiceTest {
@NonNull
private UiObject2 getUiObject(@NonNull BySelector bySelector) {
- final var uiObject = mUiDevice.wait(Until.findObject(bySelector),
- TimeUnit.SECONDS.toMillis(TIMEOUT_IN_SECONDS));
+ final var uiObject = mUiDevice.wait(Until.findObject(bySelector), TIMEOUT_MS);
assertWithMessage("UiObject with " + bySelector + " was found").that(uiObject).isNotNull();
return uiObject;
}
@@ -1137,10 +1245,10 @@ public class InputMethodServiceTest {
*
* <p>Note, neither of these are normally drawn when in three button navigation mode.
*
- * @param enabled whether the IME nav bar and IME Switcher button are drawn.
+ * @param enable whether the IME nav bar and IME Switcher button are drawn.
*/
- private void setDrawsImeNavBarAndSwitcherButton(boolean enabled) {
- final int flags = enabled ? IME_DRAWS_IME_NAV_BAR | SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0;
+ private void setDrawsImeNavBarAndSwitcherButton(boolean enable) {
+ final int flags = enable ? IME_DRAWS_IME_NAV_BAR | SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0;
mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged(flags);
}
diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java
index 558d1a7c4e8b..d4d4dcaa4f48 100644
--- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java
+++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/src/com/android/apps/inputmethod/simpleime/ims/InputMethodServiceWrapper.java
@@ -111,12 +111,6 @@ public class InputMethodServiceWrapper extends InputMethodService {
}
@Override
- public void requestHideSelf(int flags) {
- Log.i(TAG, "requestHideSelf() " + flags);
- super.requestHideSelf(flags);
- }
-
- @Override
public void onConfigurationChanged(Configuration newConfig) {
Log.i(TAG, "onConfigurationChanged() " + newConfig);
super.onConfigurationChanged(newConfig);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index f5c0de034483..e1c65d27459e 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -65,10 +65,13 @@ import android.content.pm.SigningDetails;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import androidx.annotation.Nullable;
@@ -150,6 +153,9 @@ public class PackageParserTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private File mTmpDir;
private static final File FRAMEWORK = new File("/system/framework/framework-res.apk");
private static final String TEST_APP1_APK = "PackageParserTestApp1.apk";
@@ -846,7 +852,42 @@ public class PackageParserTest {
@Test
@RequiresFlagsEnabled(android.content.res.Flags.FLAG_MANIFEST_FLAGGING)
- public void testParseWithFeatureFlagAttributes() throws Exception {
+ @DisableFlags(android.content.res.Flags.FLAG_USE_NEW_ACONFIG_STORAGE)
+ public void testParseWithFeatureFlagAttributes_oldStorage() throws Exception {
+ final File testFile = extractFile(TEST_APP8_APK);
+ try (PackageParser2 parser = new TestPackageParser2()) {
+ Map<String, Boolean> flagValues = new HashMap<>();
+ flagValues.put("my.flag1", true);
+ flagValues.put("my.flag2", false);
+ flagValues.put("my.flag3", false);
+ flagValues.put("my.flag4", true);
+ ParsingPackageUtils.getAconfigFlags().addFlagValuesForTesting(flagValues);
+
+ // The manifest has:
+ // <permission android:name="PERM1" android:featureFlag="my.flag1 " />
+ // <permission android:name="PERM2" android:featureFlag=" !my.flag2" />
+ // <permission android:name="PERM3" android:featureFlag="my.flag3" />
+ // <permission android:name="PERM4" android:featureFlag="!my.flag4" />
+ // <permission android:name="PERM5" android:featureFlag="unknown.flag" />
+ // Therefore with the above flag values, only PERM1 and PERM2 should be present.
+
+ final ParsedPackage pkg = parser.parsePackage(testFile, 0, false);
+ List<String> permissionNames =
+ pkg.getPermissions().stream().map(ParsedComponent::getName).toList();
+ assertThat(permissionNames).contains(PACKAGE_NAME + ".PERM1");
+ assertThat(permissionNames).contains(PACKAGE_NAME + ".PERM2");
+ assertThat(permissionNames).doesNotContain(PACKAGE_NAME + ".PERM3");
+ assertThat(permissionNames).doesNotContain(PACKAGE_NAME + ".PERM4");
+ assertThat(permissionNames).doesNotContain(PACKAGE_NAME + ".PERM5");
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.content.res.Flags.FLAG_MANIFEST_FLAGGING)
+ @EnableFlags(android.content.res.Flags.FLAG_USE_NEW_ACONFIG_STORAGE)
+ public void testParseWithFeatureFlagAttributes_newStorage() throws Exception {
final File testFile = extractFile(TEST_APP8_APK);
try (PackageParser2 parser = new TestPackageParser2()) {
Map<String, Boolean> flagValues = new HashMap<>();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 7d25acd7f5e7..a42116351c2d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -152,7 +152,7 @@ public class AutomaticBrightnessControllerTest {
}
@Override
- AutomaticBrightnessController.Clock createClock(boolean isEnabled) {
+ AutomaticBrightnessController.Clock createClock() {
return new AutomaticBrightnessController.Clock() {
@Override
public long uptimeMillis() {
@@ -618,39 +618,46 @@ public class AutomaticBrightnessControllerTest {
long increment = 500;
// set autobrightness to low
// t = 0
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
// t = 500
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
// t = 1000
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// t = 1500
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// t = 2000
// ensure that our reading is at 0.
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// t = 2500
// first 10000 lux sensor event reading
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 3000
// lux reading should still not yet be 10000.
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
@@ -659,45 +666,53 @@ public class AutomaticBrightnessControllerTest {
// lux has been high (10000) for 1000ms.
// lux reading should be 10000
// short horizon (ambient lux) is high, long horizon is still not high
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
// t = 4000
// stay high
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
// t = 4500
Mockito.clearInvocations(mBrightnessMappingStrategy);
mClock.fastForward(increment);
// short horizon is high, long horizon is high too
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1);
assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
// t = 5000
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 5500
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertTrue(mController.getAmbientLux() > 0.0f);
assertTrue(mController.getAmbientLux() < 10000.0f);
// t = 6000
mClock.fastForward(increment);
// ambient lux goes to 0
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
// only the values within the horizon should be kept
assertArrayEquals(new float[] {10000, 10000, 0, 0, 0}, mController.getLastSensorValues(),
EPSILON);
- assertArrayEquals(new long[] {4000, 4500, 5000, 5500, 6000},
+ assertArrayEquals(new long[]{4000 + ANDROID_SLEEP_TIME, 4500 + ANDROID_SLEEP_TIME,
+ 5000 + ANDROID_SLEEP_TIME, 5500 + ANDROID_SLEEP_TIME,
+ 6000 + ANDROID_SLEEP_TIME},
mController.getLastSensorTimestamps());
}
@@ -793,7 +808,8 @@ public class AutomaticBrightnessControllerTest {
for (int i = 0; i < 1000; i++) {
lux += increment;
mClock.fastForward(increment);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
}
int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment + 1);
@@ -807,17 +823,17 @@ public class AutomaticBrightnessControllerTest {
long sensorTimestamp = mClock.now();
for (int i = valuesCount - 1; i >= 1; i--) {
assertEquals(lux, sensorValues[i], EPSILON);
- assertEquals(sensorTimestamp, sensorTimestamps[i]);
+ assertEquals(sensorTimestamp + ANDROID_SLEEP_TIME, sensorTimestamps[i]);
lux -= increment;
sensorTimestamp -= increment;
}
assertEquals(lux, sensorValues[0], EPSILON);
- assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
+ assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG + ANDROID_SLEEP_TIME,
+ sensorTimestamps[0]);
}
@Test
public void testAmbientLuxBuffers_prunedBeyondLongHorizonExceptLatestValue() throws Exception {
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
@@ -867,7 +883,8 @@ public class AutomaticBrightnessControllerTest {
for (int i = 0; i < 20; i++) {
lux += increment1;
mClock.fastForward(increment1);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
}
int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment1 + 1);
@@ -877,7 +894,8 @@ public class AutomaticBrightnessControllerTest {
for (int i = 0; i < initialCapacity - valuesCount; i++) {
lux += increment2;
mClock.fastForward(increment2);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
}
float[] sensorValues = mController.getLastSensorValues();
@@ -890,7 +908,7 @@ public class AutomaticBrightnessControllerTest {
long sensorTimestamp = mClock.now();
for (int i = initialCapacity - 1; i >= 1; i--) {
assertEquals(lux, sensorValues[i], EPSILON);
- assertEquals(sensorTimestamp, sensorTimestamps[i]);
+ assertEquals(sensorTimestamp + ANDROID_SLEEP_TIME, sensorTimestamps[i]);
if (i >= valuesCount) {
lux -= increment2;
@@ -901,7 +919,8 @@ public class AutomaticBrightnessControllerTest {
}
}
assertEquals(lux, sensorValues[0], EPSILON);
- assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
+ assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG + ANDROID_SLEEP_TIME,
+ sensorTimestamps[0]);
}
@Test
@@ -951,25 +970,29 @@ public class AutomaticBrightnessControllerTest {
// t = 0
// Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
// t = 1000
// Lux isn't steady yet
mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
// t = 1500
// Lux isn't steady yet
mClock.fastForward(500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
// t = 2500
// Lux is steady now
mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
}
@@ -992,25 +1015,29 @@ public class AutomaticBrightnessControllerTest {
// t = 0
// Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
// t = 2000
// Lux isn't steady yet
mClock.fastForward(2000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
// t = 2500
// Lux isn't steady yet
mClock.fastForward(500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
// t = 4500
// Lux is steady now
mClock.fastForward(2000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
}
@@ -1031,19 +1058,22 @@ public class AutomaticBrightnessControllerTest {
// t = 0
// Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
// t = 500
// Lux isn't steady yet
mClock.fastForward(500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
// t = 1500
// Lux is steady now
mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
}
@@ -1068,19 +1098,22 @@ public class AutomaticBrightnessControllerTest {
// t = 0
// Initial lux
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
// t = 1000
// Lux isn't steady yet
mClock.fastForward(1000);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(1200, mController.getAmbientLux(), EPSILON);
// t = 2500
// Lux is steady now
mClock.fastForward(1500);
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500,
+ (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
assertEquals(500, mController.getAmbientLux(), EPSILON);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index aed1f9858660..db94958f769e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1066,7 +1066,6 @@ public final class DisplayPowerControllerTest {
com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
- when(mDisplayManagerFlagsMock.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
@@ -1172,7 +1171,6 @@ public final class DisplayPowerControllerTest {
com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
- when(mDisplayManagerFlagsMock.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false);
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 7d3cd8a8a9ae..38de7ce013c2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -34,6 +34,7 @@ import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REM
import static com.android.server.display.DisplayDeviceInfo.DIFF_EVERYTHING;
import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CONNECTED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
@@ -1180,13 +1181,21 @@ public class LogicalDisplayMapperTest {
assertEquals(LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
+ // Change the display committed state
+ when(mFlagsMock.isCommittedStateSeparateEventEnabled()).thenReturn(true);
+ newDisplayInfo = new DisplayInfo();
+ newDisplayInfo.committedState = STATE_OFF;
+ assertEquals(LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED,
+ mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
// Change multiple properties
newDisplayInfo = new DisplayInfo();
newDisplayInfo.refreshRateOverride = 30;
newDisplayInfo.state = STATE_OFF;
+ newDisplayInfo.committedState = STATE_OFF;
assertEquals(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED
- | LOGICAL_DISPLAY_EVENT_STATE_CHANGED,
+ | LOGICAL_DISPLAY_EVENT_STATE_CHANGED
+ | LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED,
mLogicalDisplayMapper.updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo));
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index 2ebb6c2a3ce4..ef39167dbabc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -240,7 +240,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void selectStrategyDoesNotSelectDozeStrategyWhenOffloadSessionAutoBrightnessIsEnabled() {
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
@@ -378,7 +377,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void selectStrategy_selectsAutomaticStrategyWhenValid() {
when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
@@ -409,7 +407,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() {
when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
@@ -536,7 +533,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void setAllowAutoBrightnessWhileDozing_enabledWhenConfigAndOffloadSessionAreEnabled() {
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
@@ -550,7 +546,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void setAllowAutoBrightnessWhileDozing_disabledWhenOffloadSessionFlagIsDisabled() {
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(false);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
@@ -564,7 +559,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void setAllowAutoBrightnessWhileDozing_disabledWhenABWhileDozingConfigIsDisabled() {
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
@@ -588,7 +582,6 @@ public final class DisplayBrightnessStrategySelectorTest {
@Test
public void setAllowAutoBrightnessWhileDozing_EnabledWhenFlagsAreDisabled() {
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
true);
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
@@ -600,11 +593,5 @@ public final class DisplayBrightnessStrategySelectorTest {
mDisplayBrightnessStrategySelector
.setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession);
assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
-
- when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
- when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(false);
- mDisplayBrightnessStrategySelector
- .setAllowAutoBrightnessWhileDozing(mDisplayOffloadSession);
- assertTrue(mDisplayBrightnessStrategySelector.isAllowAutoBrightnessWhileDozing());
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index db04d39e772c..eda5e8613dba 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -23,7 +23,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SKIPPED;
import static com.android.server.PackageWatchdog.MITIGATION_RESULT_SUCCESS;
@@ -34,8 +33,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import android.content.ContentResolver;
@@ -43,13 +40,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
-import android.crashrecovery.flags.Flags;
import android.os.RecoverySystem;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -60,14 +52,8 @@ import com.android.server.am.SettingsToPropertiesMapper;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -76,34 +62,19 @@ import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Test RescueParty.
*/
-@RunWith(Parameterized.class)
public class RescuePartyTest {
- @Rule
- public SetFlagsRule mSetFlagsRule;
private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
- private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
- private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
- private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
- {FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
- private static final String CALLING_PACKAGE1 = "com.package.name1";
- private static final String CALLING_PACKAGE2 = "com.package.name2";
- private static final String CALLING_PACKAGE3 = "com.package.name3";
private static final String PERSISTENT_PACKAGE = "com.persistent.package";
private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
- private static final String NAMESPACE1 = "namespace1";
- private static final String NAMESPACE2 = "namespace2";
- private static final String NAMESPACE3 = "namespace3";
- private static final String NAMESPACE4 = "namespace4";
private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG =
"persist.device_config.configuration.disable_rescue_party";
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
@@ -127,22 +98,6 @@ public class RescuePartyTest {
// Mock only sysprop apis
private PackageWatchdog.BootThreshold mSpyBootThreshold;
- @Captor
- private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor;
- @Captor
- private ArgumentCaptor<List<String>> mPackageListCaptor;
-
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getFlags() {
- return FlagsParameterization.allCombinationsOf(
- Flags.FLAG_RECOVERABILITY_DETECTION,
- Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS);
- }
-
- public RescuePartyTest(FlagsParameterization flags) {
- mSetFlagsRule = new SetFlagsRule(flags);
- }
-
@Before
public void setUp() throws Exception {
mSession =
@@ -248,7 +203,6 @@ public class RescuePartyTest {
}
@Test
- @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testBootLoopNoFlags() {
// this is old test where the flag needs to be disabled
noteBoot(1);
@@ -260,7 +214,6 @@ public class RescuePartyTest {
}
@Test
- @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testPersistentAppCrashNoFlags() {
// this is old test where the flag needs to be disabled
noteAppCrash(1, true);
@@ -396,7 +349,6 @@ public class RescuePartyTest {
}
@Test
- @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testHealthCheckLevelsNoFlags() {
// this is old test where the flag needs to be disabled
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
@@ -416,7 +368,6 @@ public class RescuePartyTest {
}
@Test
- @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testBootLoopLevelsNoFlags() {
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
@@ -425,25 +376,6 @@ public class RescuePartyTest {
}
- private void verifySettingsResets(int resetMode, String[] resetNamespaces,
- HashMap<String, Integer> configResetVerifiedTimesMap) {
- verifyOnlySettingsReset(resetMode);
- }
-
- private void verifyOnlySettingsReset(int resetMode) {
- verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
- resetMode, UserHandle.USER_SYSTEM));
- verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
- eq(resetMode), anyInt()));
- }
-
- private void verifyNoSettingsReset(int resetMode) {
- verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
- resetMode, UserHandle.USER_SYSTEM), never());
- verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
- eq(resetMode), anyInt()), never());
- }
-
private void noteBoot(int mitigationCount) {
RescuePartyObserver.getInstance(mMockContext).onExecuteBootLoopMitigation(mitigationCount);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
index 5ddd8a50135b..2e315ecd7b37 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
@@ -26,7 +26,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
@@ -54,6 +57,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.Set;
@SmallTest
@@ -74,6 +78,7 @@ public class SensitiveContentProtectionManagerServiceContentTest {
@Mock private WindowManagerInternal mWindowManager;
@Mock private MediaProjectionManager mProjectionManager;
@Mock private PackageManagerInternal mPackageManagerInternal;
+ @Mock private RoleManager mRoleManager;
private MediaProjectionInfo mMediaProjectionInfo;
@Captor
@@ -93,7 +98,8 @@ public class SensitiveContentProtectionManagerServiceContentTest {
mSensitiveContentProtectionManagerService =
new SensitiveContentProtectionManagerService(mContext);
mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager,
- mPackageManagerInternal, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage)));
+ mPackageManagerInternal, mRoleManager,
+ new ArraySet<>(Set.of(mExemptedScreenRecorderPackage)));
verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue();
mMediaProjectionInfo =
@@ -152,7 +158,7 @@ public class SensitiveContentProtectionManagerServiceContentTest {
String testAutofillService = mScreenRecorderPackage + "/com.example.SampleAutofillService";
int userId = Process.myUserHandle().getIdentifier();
Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.AUTOFILL_SERVICE, testAutofillService , userId);
+ Settings.Secure.AUTOFILL_SERVICE, testAutofillService, userId);
mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
@@ -169,6 +175,19 @@ public class SensitiveContentProtectionManagerServiceContentTest {
verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
}
+ @Test
+ public void testAppStreamingRoleHolderExemption() {
+ when(mRoleManager.getRoleHoldersAsUser(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+ mMediaProjectionInfo.getUserHandle())).thenReturn(
+ List.of(mMediaProjectionInfo.getPackageName()));
+
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ }
+
private void mockDisabledViaDeveloperOption() {
Settings.Global.putInt(
mContext.getContentResolver(),
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index 32135f1cb7fa..3c6e18f822af 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -33,6 +33,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.role.RoleManager;
+import android.companion.AssociationRequest;
import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
@@ -67,6 +69,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.Set;
@SmallTest
@@ -116,6 +119,9 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
private PackageManagerInternal mPackageManagerInternal;
@Mock
+ private RoleManager mRoleManager;
+
+ @Mock
private StatusBarNotification mNotification1;
@Mock
@@ -161,7 +167,8 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
setupSensitiveNotification();
mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager,
- mPackageManagerInternal, new ArraySet<>(Set.of(EXEMPTED_SCREEN_RECORDER_PACKAGE)));
+ mPackageManagerInternal, mRoleManager,
+ new ArraySet<>(Set.of(EXEMPTED_SCREEN_RECORDER_PACKAGE)));
// Obtain useful mMediaProjectionCallback
verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
@@ -315,6 +322,18 @@ public class SensitiveContentProtectionManagerServiceNotificationTest {
}
@Test
+ public void mediaProjectionOnStart_verifyExemptedAppStreamingPackage() {
+ MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
+ when(mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
+ mediaProjectionInfo.getUserHandle())).thenReturn(
+ List.of(mediaProjectionInfo.getPackageName()));
+
+ mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
+
+ verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ }
+
+ @Test
public void mediaProjectionOnStart_verifyExemptedRecorderPackage() {
MediaProjectionInfo mediaProjectionInfo = createExemptMediaProjectionInfo();
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 fa5847560782..4b53f1309337 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -23,18 +23,11 @@ import static com.android.server.am.ActivityManagerService.Injector;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.FrozenProcessListener;
import android.content.ComponentName;
import android.content.Context;
@@ -44,14 +37,12 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.MessageQueue;
import android.os.Process;
-import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.text.TextUtils;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
@@ -68,11 +59,9 @@ import org.mockito.Mock;
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -164,7 +153,7 @@ public final class CachedAppOptimizerTest {
app.info.uid = packageUid;
// Exact value does not mater, it can be any state for which compaction is allowed.
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.mState.setSetAdj(899);
+ app.mState.setSetAdj(940);
app.mState.setCurAdj(940);
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1ef758cf192e..340115a7d465 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -3337,6 +3337,108 @@ public class MockingOomAdjusterTests {
followUpTimeCaptor.capture());
}
+ /**
+ * For Perceptible Tasks adjustment, this solely unit-tests OomAdjuster -> onOtherActivity()
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_PERCEPTIBLE_TASKS)
+ public void testPerceptibleAdjustment() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+
+ long now = mInjector.getUptimeMillis();
+
+ // GIVEN: perceptible adjustment is NOT enabled (perceptible stop time is not set)
+ // EXPECT: zero adjustment
+ // TLDR: App is not set as a perceptible task and hence no oom_adj boosting.
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(-1);
+ assertEquals(CACHED_APP_MIN_ADJ, mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+
+ // GIVEN: perceptible adjustment is enabled (perceptible stop time is set) and
+ // elapsed time < PERCEPTIBLE_TASK_TIMEOUT
+ // EXPECT: adjustment to PERCEPTIBLE_MEDIUM_APP_ADJ
+ // TLDR: App is a perceptible task (e.g. opened from launcher) and has oom_adj boosting.
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mInjector.reset();
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(now);
+ assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ,
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+
+ // GIVEN: perceptible adjustment is enabled (perceptible stop time is set) and
+ // elapsed time > PERCEPTIBLE_TASK_TIMEOUT
+ // EXPECT: adjustment to PREVIOUS_APP_ADJ
+ // TLDR: App is a perceptible task (e.g. opened from launcher) and has oom_adj boosting, but
+ // time has elapsed and has dropped to a lower boosting of PREVIOUS_APP_ADJ
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mInjector.jumpUptimeAheadTo(OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS + 1000);
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(0);
+ assertEquals(PREVIOUS_APP_ADJ, mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+ }
+
+ /**
+ * For Perceptible Tasks adjustment, this tests overall adjustment flow.
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_PERCEPTIBLE_TASKS)
+ public void testUpdateOomAdjPerceptible() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ WindowProcessController wpc = app.getWindowProcessController();
+
+ // Set uptime to be at least the timeout time + buffer, so that we don't end up with
+ // negative stopTime in our test input
+ mInjector.jumpUptimeAheadTo(OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS + 60L * 1000L);
+ long now = mInjector.getUptimeMillis();
+ doReturn(true).when(wpc).hasActivities();
+
+ // GIVEN: perceptible adjustment is is enabled
+ // EXPECT: perceptible-act adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+ doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_IMPORTANT_BACKGROUND, PERCEPTIBLE_MEDIUM_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "perceptible-act");
+
+ // GIVEN: perceptible adjustment is is enabled and timeout has been reached
+ // EXPECT: stale-perceptible-act adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+
+ doReturn(now - OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS).when(
+ wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "stale-perceptible-act");
+
+ // GIVEN: perceptible adjustment is is disabled
+ // EXPECT: no perceptible adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+ doReturn(Long.MIN_VALUE).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
+ SCHED_GROUP_BACKGROUND, "cch-act");
+
+ // GIVEN: perceptible app is in foreground
+ // EXPECT: no perceptible adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ .when(wpc).getActivityStateFlags();
+ doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT, "vis-activity");
+ }
+
@SuppressWarnings("GuardedBy")
@Test
public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
index 8eae9c7d71fa..e030b3f19e4f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/Android.bp
@@ -32,18 +32,18 @@ android_test {
static_libs: [
"androidx.test.core",
"androidx.test.runner",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
"services.core",
"truth",
- "flag-junit",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
libs: [
- "android.test.mock.stubs.system",
"android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
@@ -55,7 +55,9 @@ android_test {
certificate: "platform",
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
+ "mts-crashrecovery",
],
+ min_sdk_version: "36",
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
index 904545bd3cc3..b4e845171a0b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
@@ -276,6 +276,7 @@ public class JobServiceContextTest {
final int jobId = 123;
mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback);
+ mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
doReturn(jobId).when(mMockJobStatus).getJobId();
mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
@@ -296,7 +297,25 @@ public class JobServiceContextTest {
mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback);
mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+ verify(mMockJobStatus, never()).setAbandoned(true);
+
+ mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+ doReturn(jobId).when(mMockJobStatus).getJobId();
+
+ mJobServiceContext.mVerb = JobServiceContext.VERB_BINDING;
+ mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+ verify(mMockJobStatus, never()).setAbandoned(true);
+ mJobServiceContext.mVerb = JobServiceContext.VERB_STARTING;
+ mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+ verify(mMockJobStatus, never()).setAbandoned(true);
+
+ mJobServiceContext.mVerb = JobServiceContext.VERB_STOPPING;
+ mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+ verify(mMockJobStatus, never()).setAbandoned(true);
+
+ mJobServiceContext.mVerb = JobServiceContext.VERB_FINISHED;
+ mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
verify(mMockJobStatus, never()).setAbandoned(true);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 2cd105ba5317..67b26c1c0b00 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -60,6 +60,8 @@ import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.SystemClock;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.MediaStore;
import android.util.SparseIntArray;
@@ -71,6 +73,7 @@ import com.android.server.job.JobSchedulerService;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -92,6 +95,9 @@ public class JobStatusTest {
private static final Uri IMAGES_MEDIA_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
private static final Uri VIDEO_MEDIA_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private JobSchedulerInternal mJobSchedulerInternal;
private MockitoSession mMockingSession;
@@ -1373,6 +1379,86 @@ public class JobStatusTest {
assertEquals("@TestNamespace@TestTag:foo", jobStatus.getBatteryName());
}
+ @Test
+ @EnableFlags({
+ com.android.server.job.Flags.FLAG_INCLUDE_TRACE_TAG_IN_JOB_NAME,
+ android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS
+ })
+ public void testJobName_NotTagNoNamespace_IncludeTraceTagInJobNameEnabled() {
+ JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setTraceTag("TestTraceTag")
+ .build();
+ JobStatus jobStatus = createJobStatus(jobInfo, null, -1, null, null);
+ assertEquals("#TestTraceTag#foo/bar", jobStatus.getBatteryName());
+ }
+
+ @Test
+ @EnableFlags({
+ com.android.server.job.Flags.FLAG_INCLUDE_TRACE_TAG_IN_JOB_NAME,
+ android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS
+ })
+ public void testJobName_NoTagWithNamespace_IncludeTraceTagInJobNameEnabled() {
+ JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setTraceTag("TestTraceTag")
+ .build();
+ JobStatus jobStatus = createJobStatus(jobInfo, null, -1, "TestNamespace", null);
+ assertEquals("#TestTraceTag#@TestNamespace@foo/bar", jobStatus.getBatteryName());
+ }
+
+ @Test
+ @EnableFlags({
+ com.android.server.job.Flags.FLAG_INCLUDE_TRACE_TAG_IN_JOB_NAME,
+ android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS
+ })
+ public void testJobName_WithTagNoNamespace_IncludeTraceTagInJobNameEnabled() {
+ JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setTraceTag("TestTraceTag")
+ .build();
+ JobStatus jobStatus = createJobStatus(jobInfo, SOURCE_PACKAGE, 0, null, "TestTag");
+ assertEquals("#TestTraceTag#TestTag:foo", jobStatus.getBatteryName());
+ }
+
+ @Test
+ @EnableFlags({
+ com.android.server.job.Flags.FLAG_INCLUDE_TRACE_TAG_IN_JOB_NAME,
+ android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS
+ })
+ public void testJobName_FilteredTraceTagEmail_IncludeTraceTagInJobNameEnabled() {
+ JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setTraceTag("test@email.com")
+ .build();
+ JobStatus jobStatus = createJobStatus(jobInfo, SOURCE_PACKAGE, 0, null, "TestTag");
+ assertEquals("#[EMAIL]#TestTag:foo", jobStatus.getBatteryName());
+ }
+
+ @Test
+ @EnableFlags({
+ com.android.server.job.Flags.FLAG_INCLUDE_TRACE_TAG_IN_JOB_NAME,
+ android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS
+ })
+ public void testJobName_FilteredTraceTagPhone_IncludeTraceTagInJobNameEnabled() {
+ JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setTraceTag("123-456-7890")
+ .build();
+ JobStatus jobStatus = createJobStatus(jobInfo, SOURCE_PACKAGE, 0, null, "TestTag");
+ assertEquals("#[PHONE]#TestTag:foo", jobStatus.getBatteryName());
+ }
+
+ @Test
+ @EnableFlags({
+ com.android.server.job.Flags.FLAG_INCLUDE_TRACE_TAG_IN_JOB_NAME,
+ android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS
+ })
+ public void testJobName_WithTagAndNamespace_IncludeTraceTagInJobNameEnabled() {
+ JobInfo jobInfo =
+ new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+ .setTraceTag("TestTraceTag")
+ .build();
+ JobStatus jobStatus =
+ createJobStatus(jobInfo, SOURCE_PACKAGE, 0, "TestNamespace", "TestTag");
+ assertEquals("#TestTraceTag#@TestNamespace@TestTag:foo", jobStatus.getBatteryName());
+ }
+
private void markExpeditedQuotaApproved(JobStatus job, boolean isApproved) {
if (job.isRequestedExpeditedJob()) {
job.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), isApproved);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index c6870adb8464..4e4b3df3c935 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -295,15 +295,15 @@ public class QuotaControllerTest {
}
private void setCharging() {
- doReturn(true).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) {
+ doReturn(true).when(mJobSchedulerService).isBatteryCharging();
mQuotaController.onBatteryStateChangedLocked();
}
}
private void setDischarging() {
- doReturn(false).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) {
+ doReturn(false).when(mJobSchedulerService).isBatteryCharging();
mQuotaController.onBatteryStateChangedLocked();
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index 5a802d9de2ff..36b064b9b090 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -30,18 +30,18 @@ android_test {
static_libs: [
"androidx.test.runner",
+ "flag-junit",
"mockito-target-extended-minus-junit4",
"services.core",
"truth",
- "flag-junit",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": ["service-crashrecovery-pre-jarjar"],
default: [],
}),
libs: [
- "android.test.mock.stubs.system",
"android.test.base.stubs.system",
+ "android.test.mock.stubs.system",
"android.test.runner.stubs.system",
],
@@ -53,9 +53,11 @@ android_test {
certificate: "platform",
platform_apis: true,
test_suites: [
- "device-tests",
"automotive-tests",
+ "device-tests",
+ "mts-crashrecovery",
],
+ min_sdk_version: "36",
}
test_module_config {
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index 347dc81c6734..fb4d81ac831c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -43,7 +43,6 @@ import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
-import android.crashrecovery.flags.Flags;
import android.os.Handler;
import android.os.MessageQueue;
import android.os.SystemProperties;
@@ -273,7 +272,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelLow_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -304,7 +302,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -335,7 +332,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelManualOnly_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -365,7 +361,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void healthCheckFailed_impactLevelLowAndHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -404,7 +399,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLow_nativeCrash_rollback()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -438,7 +432,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLow_rollbackFailedPackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -483,7 +476,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLow_rollbackAll()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -530,7 +522,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLowAndHigh_rollbackLow()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -578,7 +569,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelHigh_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId2 = 2;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
@@ -612,7 +602,6 @@ public class RollbackPackageHealthObserverTest {
*/
@Test
public void onBootLoop_impactLevelLow_onePackage() throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -637,7 +626,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -662,7 +650,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true));
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -692,7 +679,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelManualOnly_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -720,7 +706,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void onBootLoop_impactLevelLowAndHigh_onePackage()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
@@ -757,7 +742,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelLow_rollbackAll()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -802,7 +786,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -847,7 +830,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelHigh_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId2 = 2;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
@@ -882,7 +864,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelLowAndManual_rollbackLowImpactOnly()
throws PackageManager.NameNotFoundException, InterruptedException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -928,7 +909,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void execute_impactLevelManual_rollbackLowImpactOnly()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
@@ -962,7 +942,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
int rollbackId1 = 1;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
@@ -1008,7 +987,6 @@ public class RollbackPackageHealthObserverTest {
@Test
public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh()
throws PackageManager.NameNotFoundException {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true));
int rollbackId1 = 1;
VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 83a390d7f70b..4e56422ec391 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -437,6 +437,42 @@ public class NotifierTest {
}
@Test
+ public void testOnGroupChanged_perDisplayWakeByTouchEnabled() {
+ createNotifier();
+ // GIVEN per-display wake by touch is enabled and one display group has been defined with
+ // two displays
+ when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true);
+ final int groupId = 121;
+ final int displayId1 = 1221;
+ final int displayId2 = 1222;
+ final int[] displays = new int[]{displayId1, displayId2};
+ when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays));
+ when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays);
+ SparseArray<int[]> displayIdsByGroupId = new SparseArray<>();
+ displayIdsByGroupId.put(groupId, displays);
+ when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(displayIdsByGroupId);
+ mNotifier.onGroupWakefulnessChangeStarted(
+ groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000);
+ final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray();
+ expectedDisplayInteractivities.put(displayId1, true);
+ expectedDisplayInteractivities.put(displayId2, true);
+ verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities);
+
+ // WHEN display group is changed to only contain one display
+ SparseArray<int[]> newDisplayIdsByGroupId = new SparseArray<>();
+ newDisplayIdsByGroupId.put(groupId, new int[]{displayId1});
+ when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(newDisplayIdsByGroupId);
+ mNotifier.onGroupChanged();
+
+ // THEN native input manager is informed that the displays in the group have changed
+ final SparseBooleanArray expectedDisplayInteractivitiesAfterChange =
+ new SparseBooleanArray();
+ expectedDisplayInteractivitiesAfterChange.put(displayId1, true);
+ verify(mInputManagerInternal).setDisplayInteractivities(
+ expectedDisplayInteractivitiesAfterChange);
+ }
+
+ @Test
public void testOnWakeLockReleased_FrameworkStatsLogged_NoChains() {
when(mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()).thenReturn(true);
createNotifier();
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 29a17e1c85ab..ff6796561926 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -2501,6 +2501,49 @@ public class PowerManagerServiceTest {
}
@Test
+ public void testMultiDisplay_twoDisplays_onlyDefaultDisplayCanDream() {
+ final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
+ final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
+ final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener =
+ new AtomicReference<>();
+ doAnswer((Answer<Void>) invocation -> {
+ listener.set(invocation.getArgument(0));
+ return null;
+ }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any());
+ final DisplayInfo info = new DisplayInfo();
+ info.displayGroupId = nonDefaultDisplayGroupId;
+ when(mDisplayManagerInternalMock.getDisplayInfo(nonDefaultDisplay)).thenReturn(info);
+ when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(true);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1);
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setMinimumScreenOffTimeoutConfig(5);
+ createService();
+ startSystem();
+
+ listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId);
+
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_AWAKE);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ advanceTime(15000);
+
+ // Only the default display group is dreaming.
+ assertThat(mService.getWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
+ WAKEFULNESS_DREAMING);
+ assertThat(mService.getWakefulnessLocked(nonDefaultDisplayGroupId)).isEqualTo(
+ WAKEFULNESS_DOZING);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ }
+
+ @Test
public void testMultiDisplay_addNewDisplay_becomeGloballyAwakeButDefaultRemainsDozing() {
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
final int nonDefaultDisplay = Display.DEFAULT_DISPLAY + 1;
diff --git a/services/tests/powerstatstests/res/raw/battery-history.zip b/services/tests/powerstatstests/res/raw/battery-history.zip
new file mode 100644
index 000000000000..ed82ac0f79cc
--- /dev/null
+++ b/services/tests/powerstatstests/res/raw/battery-history.zip
Binary files differ
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java
new file mode 100644
index 000000000000..6be9c6d4b80c
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderPerfTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2025 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.ConditionVariable;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.CpuScalingPolicyReader;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.processor.MultiStatePowerAttributor;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Performance test")
+@Ignore("Performance experiment. Comment out @Ignore to run")
+public class BatteryUsageStatsProviderPerfTest {
+ @Rule
+ public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private final Clock mClock = new MockClock();
+ private MonotonicClock mMonotonicClock;
+ private PowerProfile mPowerProfile;
+ private CpuScalingPolicies mCpuScalingPolicies;
+ private File mDirectory;
+ private Handler mHandler;
+ private MockBatteryStatsImpl mBatteryStats;
+
+ @Before
+ public void setup() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ mPowerProfile = new PowerProfile(context);
+ mCpuScalingPolicies = new CpuScalingPolicyReader().read();
+
+ HandlerThread mHandlerThread = new HandlerThread("batterystats-handler");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+
+ // Extract accumulated battery history to ensure consistent iterations
+ mDirectory = Files.createTempDirectory("BatteryUsageStatsProviderPerfTest").toFile();
+ File historyDirectory = new File(mDirectory, "battery-history");
+ historyDirectory.mkdir();
+
+ long maxMonotonicTime = 0;
+
+ // To recreate battery-history.zip if necessary, perform these commands:
+ // cd /tmp
+ // mkdir battery-history
+ // adb pull /data/system/battery-history
+ // zip battery-history.zip battery-history/*
+ // cp battery-history.zip \
+ // $ANDROID_BUILD_TOP/frameworks/base/services/tests/powerstatstests/res/raw
+ Resources resources = context.getResources();
+ int resId = resources.getIdentifier("battery-history", "raw", context.getPackageName());
+ try (InputStream in = resources.openRawResource(resId)) {
+ try (ZipInputStream zis = new ZipInputStream(in)) {
+ ZipEntry ze;
+ while ((ze = zis.getNextEntry()) != null) {
+ if (!ze.getName().endsWith(".bh")) {
+ continue;
+ }
+ File file = new File(mDirectory, ze.getName());
+ try (OutputStream out = new FileOutputStream(
+ file)) {
+ FileUtils.copy(zis, out);
+ }
+ long timestamp = Long.parseLong(file.getName().replace(".bh", ""));
+ if (timestamp > maxMonotonicTime) {
+ maxMonotonicTime = timestamp;
+ }
+ }
+ }
+ }
+
+ mMonotonicClock = new MonotonicClock(maxMonotonicTime + 1000000000, mClock);
+ mBatteryStats = new MockBatteryStatsImpl(mClock, mDirectory);
+ }
+
+ @Test
+ public void getBatteryUsageStats_accumulated() throws IOException {
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerStateData()
+ .includeScreenStateData()
+ .includeProcessStateData()
+ .accumulated()
+ .build();
+
+ double expectedCpuPower = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+
+ waitForBackgroundThread();
+
+ BatteryUsageStatsProvider provider = createBatteryUsageStatsProvider();
+ state.resumeTiming();
+
+ BatteryUsageStats stats = provider.getBatteryUsageStats(mBatteryStats, query);
+ waitForBackgroundThread();
+
+ state.pauseTiming();
+
+ double cpuConsumedPower = stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU);
+ assertThat(cpuConsumedPower).isNonZero();
+ if (expectedCpuPower == 0) {
+ expectedCpuPower = cpuConsumedPower;
+ } else {
+ // Verify that all iterations produce the same result
+ assertThat(cpuConsumedPower).isEqualTo(expectedCpuPower);
+ }
+ stats.close();
+
+ state.resumeTiming();
+ }
+ }
+
+ private BatteryUsageStatsProvider createBatteryUsageStatsProvider() {
+ Context context = InstrumentationRegistry.getContext();
+
+ PowerStatsStore store = new PowerStatsStore(mDirectory, mHandler);
+ store.reset();
+
+ MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(context, store,
+ mPowerProfile, mCpuScalingPolicies, mPowerProfile::getBatteryCapacity);
+ return new BatteryUsageStatsProvider(context, powerAttributor, mPowerProfile,
+ mCpuScalingPolicies, store, 10000000, mClock, mMonotonicClock);
+ }
+
+ private void waitForBackgroundThread() {
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
index cb9d9b12a2fc..dcddf06f01fb 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
@@ -34,6 +34,7 @@ import static org.junit.Assert.fail;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.IntArray;
import android.util.LongArray;
import androidx.test.filters.SmallTest;
@@ -51,7 +52,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -315,8 +315,12 @@ public class CpuPowerStatsProcessorTest {
}
@Override
- void collectUids(Collection<Integer> uids) {
- uids.addAll(mUids);
+ IntArray getActiveUids() {
+ IntArray uids = new IntArray();
+ for (Integer uid : mUids) {
+ uids.add(uid);
+ }
+ return uids;
}
void verifyPowerEstimates() {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
index a232c0c7aec9..3b614bdb38ed 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
@@ -143,6 +143,8 @@ public class MultiStateStatsTest {
multiStateStats.increment(new long[]{200, 200}, 5000);
+ multiStateStats.increment(null, 6000); // No-op
+
long[] stats = new long[DIMENSION_COUNT];
multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
// (400 - 100) * 0.5 + (600 - 400) * 0.5
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index 298d27e2e8c4..879aa4893802 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -17,7 +17,6 @@
package com.android.server.security.intrusiondetection;
import static android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE;
-import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE;
import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE;
@@ -28,7 +27,6 @@ import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -37,8 +35,8 @@ import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
import android.app.admin.ConnectEvent;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DnsEvent;
-import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.ComponentName;
import android.content.Context;
@@ -53,37 +51,22 @@ import android.os.test.TestLooper;
import android.security.intrusiondetection.IIntrusionDetectionServiceCommandCallback;
import android.security.intrusiondetection.IIntrusionDetectionServiceStateCallback;
import android.security.intrusiondetection.IntrusionDetectionEvent;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.multiuser.annotations.RequireRunOnSystemUser;
-import com.android.bedstead.nene.TestApis;
-import com.android.bedstead.nene.devicepolicy.DeviceOwner;
import com.android.bedstead.permissions.CommonPermissions;
-import com.android.bedstead.permissions.PermissionContext;
import com.android.bedstead.permissions.annotations.EnsureHasPermission;
import com.android.coretests.apps.testapp.LocalIntrusionDetectionEventTransport;
import com.android.server.ServiceThread;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -107,7 +90,6 @@ public class IntrusionDetectionServiceTest {
private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
IIntrusionDetectionServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
- private static DeviceOwner sDeviceOwner;
private Context mContext;
private IntrusionDetectionEventTransportConnection mIntrusionDetectionEventTransportConnection;
@@ -124,6 +106,8 @@ public class IntrusionDetectionServiceTest {
"com.android.coretests.apps.testapp";
private static final String TEST_SERVICE = TEST_PKG + ".TestLoggingService";
+ DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
@@ -189,6 +173,7 @@ public class IntrusionDetectionServiceTest {
}
@Test
+ @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testAddStateCallback_Disabled_TwoStateCallbacks() throws RemoteException {
StateCallback scb1 = new StateCallback();
assertEquals(STATE_UNKNOWN, scb1.mState);
@@ -204,7 +189,7 @@ public class IntrusionDetectionServiceTest {
}
@Test
- @Ignore("Unit test does not run as system service UID")
+ @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testRemoveStateCallback() throws RemoteException {
mIntrusionDetectionService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
@@ -220,15 +205,19 @@ public class IntrusionDetectionServiceTest {
mIntrusionDetectionService.getBinderService().removeStateCallback(scb2);
CommandCallback ccb = new CommandCallback();
+
+ // Enable will fail; caller does not run as system server.
+ doNothing().when(mDataAggregator).enable();
mIntrusionDetectionService.getBinderService().enable(ccb);
+
mTestLooper.dispatchAll();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_DISABLED, scb2.mState);
assertNull(ccb.mErrorCode);
}
- @Ignore("Unit test does not run as system service UID")
@Test
+ @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
public void testEnable_FromDisabled_TwoStateCallbacks() throws RemoteException {
mIntrusionDetectionService.setState(STATE_DISABLED);
StateCallback scb1 = new StateCallback();
@@ -243,6 +232,9 @@ public class IntrusionDetectionServiceTest {
CommandCallback ccb = new CommandCallback();
mIntrusionDetectionService.getBinderService().enable(ccb);
+
+ // Enable will fail; caller does not run as system server.
+ doNothing().when(mDataAggregator).enable();
mTestLooper.dispatchAll();
verify(mDataAggregator, times(1)).enable();
@@ -319,7 +311,7 @@ public class IntrusionDetectionServiceTest {
assertNull(ccb.mErrorCode);
}
- @Ignore("Enable once the IntrusionDetectionEventTransportConnection is ready")
+ @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
@Test
public void testEnable_FromDisable_TwoStateCallbacks_TransportUnavailable()
throws RemoteException {
@@ -390,146 +382,6 @@ public class IntrusionDetectionServiceTest {
}
@Test
- @Ignore("Unit test does not run as system service UID")
- @RequireRunOnSystemUser
- @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
- public void testDataAggregator_AddSecurityEvent() throws Exception {
- mIntrusionDetectionService.setState(STATE_ENABLED);
- ServiceThread mockThread = spy(ServiceThread.class);
- mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
-
- // SecurityLogging generates a number of events and callbacks, so create a latch to wait for
- // the given event.
- String eventString = this.getClass().getName() + ".testSecurityEvent";
-
- final CountDownLatch latch = new CountDownLatch(1);
- // TODO: Replace this mock when the IntrusionDetectionEventTransportConnection is ready.
- doAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock input) {
- List<IntrusionDetectionEvent> receivedEvents =
- (List<IntrusionDetectionEvent>) input.getArguments()[0];
- for (IntrusionDetectionEvent event : receivedEvents) {
- if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) {
- SecurityEvent securityEvent = event.getSecurityEvent();
- Object[] eventData = (Object[]) securityEvent.getData();
- if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED
- && eventData[1].equals(eventString)) {
- latch.countDown();
- }
- }
- }
- return true;
- }
- })
- .when(mIntrusionDetectionEventTransportConnection).addData(any());
- mDataAggregator.enable();
-
- // Generate the security event.
- generateSecurityEvent(eventString);
- TestApis.devicePolicy().forceSecurityLogs();
-
- // Verify the event is received.
- mTestLooper.startAutoDispatch();
- assertTrue(latch.await(1, TimeUnit.SECONDS));
- mTestLooper.stopAutoDispatch();
-
- mDataAggregator.disable();
- }
-
- @Test
- @RequireRunOnSystemUser
- @Ignore("Unit test does not run as system service UID")
- @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
- public void testDataAggregator_AddNetworkEvent() throws Exception {
- mIntrusionDetectionService.setState(STATE_ENABLED);
- ServiceThread mockThread = spy(ServiceThread.class);
- mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
-
- // Network logging may log multiple and callbacks, so create a latch to wait for
- // the given event.
- // eventServer must be a valid domain to generate a network log event.
- String eventServer = "google.com";
- final CountDownLatch latch = new CountDownLatch(1);
- // TODO: Replace this mock when the IntrusionDetectionEventTransportConnection is ready.
- doAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock input) {
- List<IntrusionDetectionEvent> receivedEvents =
- (List<IntrusionDetectionEvent>) input.getArguments()[0];
- for (IntrusionDetectionEvent event : receivedEvents) {
- if (event.getType()
- == IntrusionDetectionEvent.NETWORK_EVENT_DNS) {
- DnsEvent dnsEvent = event.getDnsEvent();
- if (dnsEvent.getHostname().equals(eventServer)) {
- latch.countDown();
- }
- }
- }
- return true;
- }
- })
- .when(mIntrusionDetectionEventTransportConnection).addData(any());
- mDataAggregator.enable();
-
- // Generate the network event.
- generateNetworkEvent(eventServer);
- TestApis.devicePolicy().forceNetworkLogs();
-
- // Verify the event is received.
- mTestLooper.startAutoDispatch();
- assertTrue(latch.await(1, TimeUnit.SECONDS));
- mTestLooper.stopAutoDispatch();
-
- mDataAggregator.disable();
- }
-
- /** Emits a given string into security log (if enabled). */
- private void generateSecurityEvent(String eventString)
- throws IllegalArgumentException, GeneralSecurityException, IOException {
- if (eventString == null || eventString.isEmpty()) {
- throw new IllegalArgumentException(
- "Error generating security event: eventString must not be empty");
- }
-
- final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
- keyGen.initialize(
- new KeyGenParameterSpec.Builder(eventString, KeyProperties.PURPOSE_SIGN).build());
- // Emit key generation event.
- final KeyPair keyPair = keyGen.generateKeyPair();
- assertNotNull(keyPair);
-
- final KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
- ks.load(null);
- // Emit key destruction event.
- ks.deleteEntry(eventString);
- }
-
- /** Emits a given string into network log (if enabled). */
- private void generateNetworkEvent(String server) throws IllegalArgumentException, IOException {
- if (server == null || server.isEmpty()) {
- throw new IllegalArgumentException(
- "Error generating network event: server must not be empty");
- }
-
- HttpURLConnection urlConnection = null;
- int connectionTimeoutMS = 2_000;
- try (PermissionContext p = TestApis.permissions().withPermission(INTERNET)) {
- final URL url = new URL("http://" + server);
- urlConnection = (HttpURLConnection) url.openConnection();
- urlConnection.setConnectTimeout(connectionTimeoutMS);
- urlConnection.setReadTimeout(connectionTimeoutMS);
- urlConnection.getResponseCode();
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- }
- }
-
- @Test
@RequireRunOnSystemUser
@EnsureHasPermission(
android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE)
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 834fea46e505..4531b3948495 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -199,6 +199,10 @@
<service android:name="com.android.server.job.MockBiasJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
+ <activity
+ android:name="android.app.Activity"
+ android:exported="false" />
+
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/>
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/>
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 8dfd54fe38bc..42b84bdc51e6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -46,12 +46,11 @@ import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityActi
import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_INPUT;
import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -65,6 +64,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
@@ -90,14 +90,20 @@ import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.accessibility.IWindowSurfaceInfoCallback;
+import android.window.ScreenCapture;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
import com.android.server.accessibility.magnification.MagnificationProcessor;
@@ -105,6 +111,7 @@ import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -121,6 +128,10 @@ import java.util.concurrent.Callable;
* Tests for the AbstractAccessibilityServiceConnection
*/
public class AbstractAccessibilityServiceConnectionTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final ComponentName COMPONENT_NAME = new ComponentName(
"com.android.server.accessibility", ".AbstractAccessibilityServiceConnectionTest");
private static final String PACKAGE_NAME1 = "com.android.server.accessibility1";
@@ -264,7 +275,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void getCapabilities() {
- assertThat(mServiceConnection.getCapabilities(), is(A11Y_SERVICE_CAPABILITY));
+ assertThat(mServiceConnection.getCapabilities()).isEqualTo(A11Y_SERVICE_CAPABILITY);
}
@Test
@@ -329,7 +340,7 @@ public class AbstractAccessibilityServiceConnectionTest {
0, null, 0);
mServiceConnection.setServiceInfo(serviceInfo);
- assertThat(mServiceConnection.canReceiveEventsLocked(), is(true));
+ assertThat(mServiceConnection.canReceiveEventsLocked()).isTrue();
}
@Test
@@ -348,9 +359,11 @@ public class AbstractAccessibilityServiceConnectionTest {
mServiceConnection.getWindows();
assertEquals(2, allWindows.size());
- assertThat(allWindows.get(Display.DEFAULT_DISPLAY), is(mA11yWindowInfos));
+ assertThat(allWindows.get(Display.DEFAULT_DISPLAY))
+ .containsExactlyElementsIn(mA11yWindowInfos);
assertEquals(2, allWindows.get(Display.DEFAULT_DISPLAY).size());
- assertThat(allWindows.get(SECONDARY_DISPLAY_ID), is(mA11yWindowInfosOnSecondDisplay));
+ assertThat(allWindows.get(SECONDARY_DISPLAY_ID))
+ .containsExactlyElementsIn(mA11yWindowInfosOnSecondDisplay);
assertEquals(1, allWindows.get(SECONDARY_DISPLAY_ID).size());
}
@@ -358,12 +371,12 @@ public class AbstractAccessibilityServiceConnectionTest {
public void getWindows_returnNull() {
// no canRetrieveWindows, should return null
when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(false);
- assertThat(mServiceConnection.getWindows(), is(nullValue()));
+ assertThat(mServiceConnection.getWindows()).isNull();
// no checkAccessibilityAccess, should return null
when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(true);
when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
- assertThat(mServiceConnection.getWindows(), is(nullValue()));
+ assertThat(mServiceConnection.getWindows()).isNull();
}
@Test
@@ -378,19 +391,19 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void getWindow() {
- assertThat(mServiceConnection.getWindow(WINDOWID), is(mA11yWindowInfos.get(0)));
+ assertThat(mServiceConnection.getWindow(WINDOWID)).isEqualTo(mA11yWindowInfos.get(0));
}
@Test
public void getWindow_returnNull() {
// no canRetrieveWindows, should return null
when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(false);
- assertThat(mServiceConnection.getWindow(WINDOWID), is(nullValue()));
+ assertThat(mServiceConnection.getWindow(WINDOWID)).isNull();
// no checkAccessibilityAccess, should return null
when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(true);
when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
- assertThat(mServiceConnection.getWindow(WINDOWID), is(nullValue()));
+ assertThat(mServiceConnection.getWindow(WINDOWID)).isNull();
}
@Test
@@ -405,8 +418,8 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void getWindow_onNonDefaultDisplay() {
- assertThat(mServiceConnection.getWindow(WINDOWID_ONSECONDDISPLAY),
- is(mA11yWindowInfosOnSecondDisplay.get(0)));
+ assertThat(mServiceConnection.getWindow(WINDOWID_ONSECONDDISPLAY))
+ .isEqualTo(mA11yWindowInfosOnSecondDisplay.get(0));
}
@Test
@@ -415,9 +428,9 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSecurityPolicy.canGetAccessibilityNodeInfoLocked(
USER_ID, mServiceConnection, WINDOWID)).thenReturn(false);
for (int i = 0; i < mFindA11yNodesFunctions.length; i++) {
- assertThat(mFindA11yNodesFunctions[i].call(), is(nullValue()));
+ assertThat(mFindA11yNodesFunctions[i].call()).isNull();
}
- assertThat(mPerformA11yAction.call(), is(false));
+ assertThat(mPerformA11yAction.call()).isFalse();
verifyNoMoreInteractions(mMockIA11yInteractionConnection);
verify(mMockSecurityPolicy, never()).computeValidReportedPackages(any(), anyInt());
@@ -428,9 +441,9 @@ public class AbstractAccessibilityServiceConnectionTest {
throws Exception {
when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
for (int i = 0; i < mFindA11yNodesFunctions.length; i++) {
- assertThat(mFindA11yNodesFunctions[i].call(), is(nullValue()));
+ assertThat(mFindA11yNodesFunctions[i].call()).isNull();
}
- assertThat(mPerformA11yAction.call(), is(false));
+ assertThat(mPerformA11yAction.call()).isFalse();
verifyNoMoreInteractions(mMockIA11yInteractionConnection);
verify(mMockSecurityPolicy, never()).computeValidReportedPackages(any(), anyInt());
@@ -441,9 +454,9 @@ public class AbstractAccessibilityServiceConnectionTest {
throws Exception {
when(mMockA11yWindowManager.getConnectionLocked(USER_ID, WINDOWID)).thenReturn(null);
for (int i = 0; i < mFindA11yNodesFunctions.length; i++) {
- assertThat(mFindA11yNodesFunctions[i].call(), is(nullValue()));
+ assertThat(mFindA11yNodesFunctions[i].call()).isNull();
}
- assertThat(mPerformA11yAction.call(), is(false));
+ assertThat(mPerformA11yAction.call()).isFalse();
verifyNoMoreInteractions(mMockIA11yInteractionConnection);
verify(mMockSecurityPolicy, never()).computeValidReportedPackages(any(), anyInt());
@@ -562,7 +575,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable())
.thenReturn(true);
final boolean result = mServiceConnection.isFingerprintGestureDetectionAvailable();
- assertThat(result, is(true));
+ assertThat(result).isTrue();
}
@Test
@@ -573,14 +586,14 @@ public class AbstractAccessibilityServiceConnectionTest {
// Return false if device does not support fingerprint
when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(false);
boolean result = mServiceConnection.isFingerprintGestureDetectionAvailable();
- assertThat(result, is(false));
+ assertThat(result).isFalse();
// Return false if service does not have flag
when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
mSpyServiceInfo.flags = A11Y_SERVICE_FLAG & ~FLAG_REQUEST_FINGERPRINT_GESTURES;
mServiceConnection.setServiceInfo(mSpyServiceInfo);
result = mServiceConnection.isFingerprintGestureDetectionAvailable();
- assertThat(result, is(false));
+ assertThat(result).isFalse();
}
@Test
@@ -590,7 +603,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockMagnificationProcessor.getScale(displayId)).thenReturn(scale);
final float result = mServiceConnection.getMagnificationScale(displayId);
- assertThat(result, is(scale));
+ assertThat(result).isEqualTo(scale);
}
@Test
@@ -601,7 +614,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final float result = mServiceConnection.getMagnificationScale(displayId);
- assertThat(result, is(1.0f));
+ assertThat(result).isEqualTo(1.0f);
}
@Test
@@ -616,7 +629,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final Region result = mServiceConnection.getMagnificationRegion(displayId);
- assertThat(result.isEmpty(), is(true));
+ assertWithMessage("Non-empty region: " + result).that(result.isEmpty()).isTrue();
}
@Test
@@ -642,7 +655,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final float result = mServiceConnection.getMagnificationCenterX(displayId);
- assertThat(result, is(0.0f));
+ assertThat(result).isEqualTo(0.0f);
}
@Test
@@ -654,7 +667,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final float result = mServiceConnection.getMagnificationCenterY(displayId);
- assertThat(result, is(0.0f));
+ assertThat(result).isEqualTo(0.0f);
}
@Test
@@ -664,7 +677,7 @@ public class AbstractAccessibilityServiceConnectionTest {
true);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
- assertThat(result, is(true));
+ assertThat(result).isTrue();
}
@Test
@@ -675,7 +688,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
- assertThat(result, is(false));
+ assertThat(result).isFalse();
}
@Test
@@ -686,7 +699,7 @@ public class AbstractAccessibilityServiceConnectionTest {
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
- assertThat(result, is(false));
+ assertThat(result).isFalse();
}
@Test
@@ -765,6 +778,134 @@ public class AbstractAccessibilityServiceConnectionTest {
== bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
}
+ private void setPreinstalledA11yTool(boolean isPreinstalledA11yTool) {
+ when(mSpyServiceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp())
+ .thenReturn(isPreinstalledA11yTool);
+ when(mSpyServiceInfo.isAccessibilityTool()).thenReturn(isPreinstalledA11yTool);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
+ public void takeScreenshot_standardService_cannotCaptureSecureLayers() {
+ setPreinstalledA11yTool(false);
+
+ takeScreenshotOfDisplay();
+
+ final ArgumentCaptor<ScreenCapture.CaptureArgs> displayArgsCaptor =
+ ArgumentCaptor.forClass(ScreenCapture.CaptureArgs.class);
+ verify(mMockWindowManagerInternal).captureDisplay(
+ eq(Display.DEFAULT_DISPLAY), displayArgsCaptor.capture(), any());
+ assertThat(displayArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
+ public void takeScreenshot_preinstalledA11yTool_canCaptureSecureLayers() {
+ setPreinstalledA11yTool(true);
+
+ takeScreenshotOfDisplay();
+
+ final ArgumentCaptor<ScreenCapture.CaptureArgs> displayArgsCaptor =
+ ArgumentCaptor.forClass(ScreenCapture.CaptureArgs.class);
+ verify(mMockWindowManagerInternal).captureDisplay(
+ anyInt(), displayArgsCaptor.capture(), any());
+ assertThat(displayArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
+ }
+
+ private void takeScreenshotOfDisplay() {
+ when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
+ when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
+
+ final DisplayManager displayManager = new DisplayManager(mMockContext);
+ when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(displayManager);
+
+ mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY,
+ new RemoteCallback(mMockListener));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
+ public void takeScreenshotOfWindow_standardWindow_standardService_cannotCaptureSecureLayers()
+ throws Exception {
+ setPreinstalledA11yTool(false);
+
+ takeScreenshotOfWindow(/*windowFlags=*/0);
+
+ // Screenshot was allowed
+ final ArgumentCaptor<ScreenCapture.LayerCaptureArgs> layerArgsCaptor =
+ ArgumentCaptor.forClass(ScreenCapture.LayerCaptureArgs.class);
+ verify(mMockSystemSupport).performScreenCapture(layerArgsCaptor.capture(), any());
+ // ...without secure layers included
+ assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
+ // No error sent to callback
+ verifyZeroInteractions(mMockCallback);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
+ public void takeScreenshotOfWindow_standardWindow_preinstalledA11yTool_canCaptureSecureLayers()
+ throws Exception {
+ setPreinstalledA11yTool(true);
+
+ takeScreenshotOfWindow(/*windowFlags=*/0);
+
+ // Screenshot was allowed
+ final ArgumentCaptor<ScreenCapture.LayerCaptureArgs> layerArgsCaptor =
+ ArgumentCaptor.forClass(ScreenCapture.LayerCaptureArgs.class);
+ verify(mMockSystemSupport).performScreenCapture(layerArgsCaptor.capture(), any());
+ // ...with secure layers included
+ assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
+ // No error sent to callback
+ verifyZeroInteractions(mMockCallback);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
+ public void takeScreenshotOfWindow_secureWindow_standardService_sendsCallbackError()
+ throws Exception {
+ setPreinstalledA11yTool(false);
+
+ takeScreenshotOfWindow(WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Screenshot was not allowed
+ verify(mMockSystemSupport, never()).performScreenCapture(any(), any());
+ // Error sent to callback
+ verify(mMockCallback).sendTakeScreenshotOfWindowError(
+ AccessibilityService.ERROR_TAKE_SCREENSHOT_SECURE_WINDOW, INTERACTION_ID);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
+ public void takeScreenshotOfWindow_secureWindow_preinstalledA11yTool_canCaptureSecureLayers()
+ throws Exception {
+ setPreinstalledA11yTool(true);
+
+ takeScreenshotOfWindow(WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Screenshot was allowed
+ final ArgumentCaptor<ScreenCapture.LayerCaptureArgs> layerArgsCaptor =
+ ArgumentCaptor.forClass(ScreenCapture.LayerCaptureArgs.class);
+ verify(mMockSystemSupport).performScreenCapture(layerArgsCaptor.capture(), any());
+ // ...with secure layers included
+ assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
+ // No error sent to callback
+ verifyZeroInteractions(mMockCallback);
+ }
+
+ private void takeScreenshotOfWindow(int windowFlags) throws Exception {
+ when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
+ when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
+
+ mServiceConnection.takeScreenshotOfWindow(
+ WINDOWID, INTERACTION_ID, /*listener=*/null, mMockCallback);
+ final ArgumentCaptor<IWindowSurfaceInfoCallback> windowSurfaceCallbackCaptor =
+ ArgumentCaptor.forClass(IWindowSurfaceInfoCallback.class);
+ verify(mMockIA11yInteractionConnection).getWindowSurfaceInfo(
+ windowSurfaceCallbackCaptor.capture());
+ windowSurfaceCallbackCaptor.getValue().provideWindowSurfaceInfo(
+ windowFlags, /*appUid=*/0, new SurfaceControl());
+ }
+
private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
int feedbackType, int flags, String[] packageNames, int notificationTimeout) {
serviceInfo.eventTypes = eventType;
@@ -837,8 +978,8 @@ public class AbstractAccessibilityServiceConnectionTest {
ArgumentCaptor.forClass(AccessibilityNodeInfo.class);
verify(mMockCallback).setFindAccessibilityNodeInfoResult(captor.capture(),
eq(INTERACTION_ID));
- assertThat(captor.getValue().getActionList(),
- hasItems(AccessibilityAction.ACTION_CLICK, AccessibilityAction.ACTION_EXPAND));
+ assertThat(captor.getValue().getActionList()).containsAtLeast(
+ AccessibilityAction.ACTION_CLICK, AccessibilityAction.ACTION_EXPAND);
}
private static class TestAccessibilityServiceConnection
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 0227ef1d2dc0..0745c68fd337 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
@@ -326,6 +327,120 @@ public class AutoclickControllerTest {
assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
}
+ @Test
+ public void smallJitteryMovement_doesNotTriggerClick() {
+ // Initial hover move to set an anchor point.
+ MotionEvent initialHoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 40f,
+ /* metaState= */ 0);
+ initialHoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(initialHoverMove, initialHoverMove, /* policyFlags= */ 0);
+
+ // Get the initial scheduled click time.
+ long initialScheduledTime = mController.mClickScheduler.getScheduledClickTimeForTesting();
+
+ // Simulate small, jittery movements (all within the default slop).
+ MotionEvent jitteryMove1 = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 150,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 31f, // Small change in x
+ /* y= */ 41f, // Small change in y
+ /* metaState= */ 0);
+ jitteryMove1.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(jitteryMove1, jitteryMove1, /* policyFlags= */ 0);
+
+ MotionEvent jitteryMove2 = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 200,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30.5f, // Small change in x
+ /* y= */ 39.8f, // Small change in y
+ /* metaState= */ 0);
+ jitteryMove2.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(jitteryMove2, jitteryMove2, /* policyFlags= */ 0);
+
+ // Verify that the scheduled click time has NOT changed.
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting())
+ .isEqualTo(initialScheduledTime);
+ }
+
+ @Test
+ public void singleSignificantMovement_triggersClick() {
+ // Initial hover move to set an anchor point.
+ MotionEvent initialHoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 40f,
+ /* metaState= */ 0);
+ initialHoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(initialHoverMove, initialHoverMove, /* policyFlags= */ 0);
+
+ // Get the initial scheduled click time.
+ long initialScheduledTime = mController.mClickScheduler.getScheduledClickTimeForTesting();
+
+ // Simulate a single, significant movement (greater than the default slop).
+ MotionEvent significantMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 150,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 60f, // Significant change in x (30f difference)
+ /* y= */ 70f, // Significant change in y (30f difference)
+ /* metaState= */ 0);
+ significantMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(significantMove, significantMove, /* policyFlags= */ 0);
+
+ // Verify that the scheduled click time has changed (click was rescheduled).
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting())
+ .isNotEqualTo(initialScheduledTime);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void pauseButton_flagOn_clickNotTriggeredWhenPaused() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Pause autoclick.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+ // Verify there is not a pending click.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+ // Resume autoclick.
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(false);
+
+ // Send initial move event again. Because this is the first recorded move, a click won't be
+ // scheduled.
+ injectFakeMouseActionHoverMoveEvent();
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+ // Send move again to trigger click and verify there is now a pending click.
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
+ }
+
private void injectFakeMouseActionHoverMoveEvent() {
MotionEvent event = getFakeMotionHoverMoveEvent();
event.setSource(InputDevice.SOURCE_MOUSE);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index 00cc7264c1d0..ba672dcd299b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -30,6 +30,7 @@ import android.graphics.drawable.GradientDrawable;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
+import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
@@ -63,8 +64,11 @@ public class AutoclickTypePanelTest {
private LinearLayout mDoubleClickButton;
private LinearLayout mDragButton;
private LinearLayout mScrollButton;
+ private LinearLayout mPauseButton;
+ private LinearLayout mPositionButton;
private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
+ private boolean mPaused;
private final ClickPanelControllerInterface clickPanelController =
new ClickPanelControllerInterface() {
@@ -74,7 +78,9 @@ public class AutoclickTypePanelTest {
}
@Override
- public void toggleAutoclickPause() {}
+ public void toggleAutoclickPause(boolean paused) {
+ mPaused = paused;
+ }
};
@Before
@@ -91,6 +97,8 @@ public class AutoclickTypePanelTest {
contentView.findViewById(R.id.accessibility_autoclick_double_click_layout);
mScrollButton = contentView.findViewById(R.id.accessibility_autoclick_scroll_layout);
mDragButton = contentView.findViewById(R.id.accessibility_autoclick_drag_layout);
+ mPauseButton = contentView.findViewById(R.id.accessibility_autoclick_pause_layout);
+ mPositionButton = contentView.findViewById(R.id.accessibility_autoclick_position_layout);
}
@Test
@@ -106,6 +114,7 @@ public class AutoclickTypePanelTest {
assertThat(mDoubleClickButton.getVisibility()).isEqualTo(View.GONE);
assertThat(mDragButton.getVisibility()).isEqualTo(View.GONE);
assertThat(mScrollButton.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mPauseButton.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -124,6 +133,9 @@ public class AutoclickTypePanelTest {
assertThat(mDoubleClickButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mDragButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mScrollButton.getVisibility()).isEqualTo(View.VISIBLE);
+
+ // Pause button is always visible.
+ assertThat(mPauseButton.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -142,6 +154,9 @@ public class AutoclickTypePanelTest {
assertThat(mLeftClickButton.getVisibility()).isEqualTo(View.GONE);
assertThat(mDoubleClickButton.getVisibility()).isEqualTo(View.GONE);
assertThat(mDragButton.getVisibility()).isEqualTo(View.GONE);
+
+ // Pause button is always visible.
+ assertThat(mPauseButton.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -166,9 +181,50 @@ public class AutoclickTypePanelTest {
assertThat(mActiveClickType).isEqualTo(AUTOCLICK_TYPE_SCROLL);
}
+ @Test
+ public void moveToNextCorner_positionButton_rotatesThroughAllPositions() {
+ // Define all positions in sequence
+ int[][] expectedPositions = {
+ {0, Gravity.END | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90},
+ {1, Gravity.START | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90},
+ {2, Gravity.START | Gravity.TOP, /*x=*/ 15, /*y=*/ 30},
+ {3, Gravity.END | Gravity.TOP, /*x=*/ 15, /*y=*/ 30},
+ {0, Gravity.END | Gravity.BOTTOM, /*x=*/ 15, /*y=*/ 90}
+ };
+
+ // Check initial position
+ verifyPanelPosition(expectedPositions[0]);
+
+ // Move through all corners.
+ for (int i = 1; i < expectedPositions.length; i++) {
+ mPositionButton.callOnClick();
+ verifyPanelPosition(expectedPositions[i]);
+ }
+ }
+
+ @Test
+ public void pauseButton_onClick() {
+ mPauseButton.callOnClick();
+ assertThat(mPaused).isTrue();
+ assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+ mPauseButton.callOnClick();
+ assertThat(mPaused).isFalse();
+ assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+ }
+
private void verifyButtonHasSelectedStyle(@NonNull LinearLayout button) {
GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
assertThat(gradientDrawable.getColor().getDefaultColor())
.isEqualTo(mTestableContext.getColor(R.color.materialColorPrimary));
}
+
+ private void verifyPanelPosition(int[] expectedPosition) {
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParams();
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting()).isEqualTo(
+ expectedPosition[0]);
+ assertThat(params.gravity).isEqualTo(expectedPosition[1]);
+ assertThat(params.x).isEqualTo(expectedPosition[2]);
+ assertThat(params.y).isEqualTo(expectedPosition[3]);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java
index b5a538fa09f8..c7da27420cbb 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceInventoryTest.java
@@ -103,7 +103,7 @@ public class AudioDeviceInventoryTest {
// NOTE: for now this is only when flag asDeviceConnectionFailure is true
if (asDeviceConnectionFailure()) {
when(mSpyAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT))
+ AudioSystem.AUDIO_FORMAT_DEFAULT, false /*deviceSwitch*/))
.thenReturn(AudioSystem.AUDIO_STATUS_ERROR);
runWithBluetoothPrivilegedPermission(
() -> mDevInventory.onSetBtActiveDevice(/*btInfo*/ btInfo,
@@ -115,7 +115,7 @@ public class AudioDeviceInventoryTest {
// test that the device is added when AudioSystem returns AUDIO_STATUS_OK
// when setDeviceConnectionState is called for the connection
when(mSpyAudioSystem.setDeviceConnectionState(ada, AudioSystem.DEVICE_STATE_AVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT))
+ AudioSystem.AUDIO_FORMAT_DEFAULT, false /*deviceSwitch*/))
.thenReturn(AudioSystem.AUDIO_STATUS_OK);
runWithBluetoothPrivilegedPermission(
() -> mDevInventory.onSetBtActiveDevice(/*btInfo*/ btInfo,
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index ce59a86c6ca3..39e7d727f7c5 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -51,9 +51,9 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
// Overrides of AudioSystemAdapter
@Override
public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state,
- int codecFormat) {
- Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, 0x%s",
- attributes.toString(), state, Integer.toHexString(codecFormat)));
+ int codecFormat, boolean deviceSwitch) {
+ Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, 0x%s %b",
+ attributes.toString(), state, Integer.toHexString(codecFormat), deviceSwitch));
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index e5fac7ac5e0c..00b0c558b4e3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -263,6 +263,11 @@ public class DpmMockContext extends MockContext {
}
@Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mMockSystemServices.packageManager;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index fca0cfbc7d2f..b2d48a77386f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
+import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
@@ -25,6 +26,7 @@ import static com.android.server.hdmi.HdmiCecFeatureAction.DELAY_GIVE_AUDIO_STAT
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -122,6 +124,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
@Before
public void setUp() throws RemoteException {
+ assumeTrue("Test requires FEATURE_HDMI_CEC",
+ InstrumentationRegistry.getTargetContext().getPackageManager()
+ .hasSystemFeature(FEATURE_HDMI_CEC));
MockitoAnnotations.initMocks(this);
mContextSpy = spy(new ContextWrapper(
@@ -432,7 +437,7 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
- any(), any(), anyBoolean());
+ anyBoolean(), any(), any());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
index ec44a918f8e8..f44517a47f55 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
@@ -112,7 +112,7 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
- any(), any(), anyBoolean());
+ anyBoolean(), any(), any());
}
@@ -135,7 +135,7 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
- any(), any(), anyBoolean());
+ anyBoolean(), any(), any());
}
@Test
@@ -160,7 +160,7 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
.setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
.setMinVolumeIndex(AudioStatus.MIN_VOLUME)
.build()),
- any(), any(), anyBoolean());
+ anyBoolean(), any(), any());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
index 7294ba62cdae..90f94cb4b596 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
@@ -183,9 +183,9 @@ public class FakeAudioFramework {
public void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
+ boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnAudioDeviceVolumeChangedListener vclistener,
- boolean handlesVolumeAdjustment) {
+ @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
setVolumeBehaviorHelper(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
@@ -193,9 +193,9 @@ public class FakeAudioFramework {
public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
+ boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnAudioDeviceVolumeChangedListener vclistener,
- boolean handlesVolumeAdjustment) {
+ @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
setVolumeBehaviorHelper(device,
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index f74e2ace7ae3..563baacf5811 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -66,6 +66,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -1033,6 +1034,7 @@ public class HdmiCecLocalDeviceTvTest {
}
@Test
+ @Ignore("b/360768278")
public void onHotplug_doNotSend_systemAudioModeRequestWithParameter(){
// Add a device to the network and assert that this device is included in the list of
// devices.
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 87c9db2fe565..acbce36c3d7f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -354,14 +354,20 @@ public abstract class BaseLockSettingsServiceTests {
@After
public void tearDown_baseServices() throws Exception {
- mStorage.closeDatabase();
+ if (mStorage != null) {
+ mStorage.closeDatabase();
+ }
File db = InstrumentationRegistry.getContext().getDatabasePath("locksettings.db");
assertTrue(!db.exists() || db.delete());
- File storageDir = mStorage.mStorageDir;
- assertTrue(FileUtils.deleteContents(storageDir));
+ if (mStorage != null) {
+ File storageDir = mStorage.mStorageDir;
+ assertTrue(FileUtils.deleteContents(storageDir));
+ }
- mPasswordSlotManager.cleanup();
+ if (mPasswordSlotManager != null) {
+ mPasswordSlotManager.cleanup();
+ }
}
protected void flushHandlerTasks() {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 02b86db6ab6f..387b89a41eba 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -124,7 +124,9 @@ public class LockSettingsStorageTests {
@After
public void tearDown() throws Exception {
- mStorage.closeDatabase();
+ if (mStorage != null) {
+ mStorage.closeDatabase();
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
index 2faf6a2b29d1..2c2b9374fdf9 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
@@ -49,7 +49,9 @@ public class PasswordSlotManagerTests {
@After
public void tearDown() throws Exception {
- mManager.cleanup();
+ if (mManager != null) {
+ mManager.cleanup();
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 1514de04fb08..5add74e5b69e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -156,9 +156,12 @@ public class KeySyncTaskTest {
@After
public void tearDown() {
- mRecoverableKeyStoreDb.close();
- mDatabaseFile.delete();
-
+ if (mRecoverableKeyStoreDb != null) {
+ mRecoverableKeyStoreDb.close();
+ }
+ if (mDatabaseFile != null) {
+ mDatabaseFile.delete();
+ }
File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
SNAPSHOT_TOP_LEVEL_DIRECTORY);
FileUtils.deleteContentsAndDir(file);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index c09e09c8404f..46eaba7dace6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -117,8 +117,12 @@ public class PlatformKeyManagerTest {
@After
public void tearDown() {
- mRecoverableKeyStoreDb.close();
- mDatabaseFile.delete();
+ if (mRecoverableKeyStoreDb != null) {
+ mRecoverableKeyStoreDb.close();
+ }
+ if (mDatabaseFile != null) {
+ mDatabaseFile.delete();
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index 64130266b2c4..e6a6e36e75d6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -89,8 +89,12 @@ public class RecoverableKeyGeneratorTest {
keyStore.load(/*param=*/ null);
keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
- mRecoverableKeyStoreDb.close();
- mDatabaseFile.delete();
+ if (mRecoverableKeyStoreDb != null) {
+ mRecoverableKeyStoreDb.close();
+ }
+ if (mDatabaseFile != null) {
+ mDatabaseFile.delete();
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 7641fb957cc8..878c838e734b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -230,9 +230,15 @@ public class RecoverableKeyStoreManagerTest {
@After
public void tearDown() {
- mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
- mRecoverableKeyStoreDb.close();
- mDatabaseFile.delete();
+ if (mRemoteLockscreenValidationSessionStorage != null) {
+ mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
+ }
+ if (mRecoverableKeyStoreDb != null) {
+ mRecoverableKeyStoreDb.close();
+ }
+ if (mDatabaseFile != null) {
+ mDatabaseFile.delete();
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
index bbd9223718ae..fb98fab52ca0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -18,6 +18,8 @@ package com.android.server.locksettings.recoverablekeystore.storage;
import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
@@ -36,8 +38,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecoverableKeyStoreDbHelperTest {
@@ -110,7 +110,9 @@ public class RecoverableKeyStoreDbHelperTest {
@After
public void tearDown() throws Exception {
- mDatabase.close();
+ if (mDatabase != null) {
+ mDatabase.close();
+ }
}
private void createV2Tables() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 8bc14fc54ae1..a77d8bcd3875 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -72,8 +72,12 @@ public class RecoverableKeyStoreDbTest {
@After
public void tearDown() {
- mRecoverableKeyStoreDb.close();
- mDatabaseFile.delete();
+ if (mRecoverableKeyStoreDb != null) {
+ mRecoverableKeyStoreDb.close();
+ }
+ if (mDatabaseFile != null) {
+ mDatabaseFile.delete();
+ }
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java
index b2e296a36b93..2912a0762761 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java
@@ -19,6 +19,7 @@ package com.android.server.om;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.content.om.OverlayConstraint.TYPE_DEVICE_ID;
import static android.content.om.OverlayConstraint.TYPE_DISPLAY_ID;
+import static android.util.TypedValue.TYPE_STRING;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
@@ -28,6 +29,9 @@ import static junit.framework.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
+import android.app.Activity;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.Context;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
@@ -35,12 +39,12 @@ import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.content.om.OverlayManagerTransaction;
import android.content.res.Flags;
+import android.content.res.Resources;
import android.os.UserHandle;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.util.TypedValue;
+import android.view.Display;
+import android.virtualdevice.cts.common.VirtualDeviceRule;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
@@ -53,20 +57,28 @@ import org.junit.runner.RunWith;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeoutException;
@RunWith(JUnitParamsRunner.class)
public class OverlayConstraintsTests {
+ private static final String RESOURCE_NAME = "string/module_2_name";
+ private static final String RESOURCE_DEFAULT_VALUE = "module_2_name";
+ private static final String RESOURCE_OVERLAID_VALUE = "hello";
+ private static final long TIMEOUT_MILLIS = 2000L;
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final VirtualDeviceRule mVirtualDeviceRule = VirtualDeviceRule.createDefault();
private OverlayManager mOverlayManager;
private UserHandle mUserHandle;
private OverlayIdentifier mOverlayIdentifier = null;
+ private final String mPackageName = getApplicationContext().getPackageName();
@Before
public void setUp() throws Exception {
- mOverlayManager = getApplicationContext().getSystemService(OverlayManager.class);
+ final Context context = getApplicationContext();
+ mOverlayManager = context.getSystemService(OverlayManager.class);
mUserHandle = UserHandle.of(UserHandle.myUserId());
}
@@ -79,6 +91,7 @@ public class OverlayConstraintsTests {
.build();
mOverlayManager.commit(transaction);
mOverlayIdentifier = null;
+ waitForResourceValue(RESOURCE_DEFAULT_VALUE, getApplicationContext());
}
}
@@ -161,13 +174,161 @@ public class OverlayConstraintsTests {
List.of(new OverlayConstraint(TYPE_DISPLAY_ID, DEFAULT_DISPLAY))));
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithoutConstraints_appliesOverlayWithoutConstraints()
+ throws Exception {
+ enableOverlay(Collections.emptyList());
+
+ // Assert than the overlay is applied for both default device context and virtual
+ // device context.
+ final Context context = getApplicationContext();
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context);
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceRule.createManagedVirtualDevice();
+ final Context deviceContext = context.createDeviceContext(virtualDevice.getDeviceId());
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, deviceContext);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_withTypeDeviceId_appliesOverlayWithConstraints()
+ throws Exception {
+ final int deviceId1 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId();
+ final int deviceId2 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId();
+ enableOverlay(List.of(new OverlayConstraint(TYPE_DEVICE_ID, deviceId1),
+ new OverlayConstraint(TYPE_DEVICE_ID, deviceId2)));
+
+ // Assert than the overlay is not applied for contexts not associated with the above
+ // devices.
+ final Context context = getApplicationContext();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context);
+ final int deviceId3 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDeviceContext(deviceId3));
+
+ // Assert than the overlay is applied for contexts associated with the above devices.
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDeviceContext(deviceId1));
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDeviceContext(deviceId2));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_withTypeDisplayId_appliesOverlayWithConstraints()
+ throws Exception {
+ final Display display1 =
+ mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay();
+ final Display display2 =
+ mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay();
+ enableOverlay(List.of(new OverlayConstraint(TYPE_DISPLAY_ID, display1.getDisplayId()),
+ new OverlayConstraint(TYPE_DISPLAY_ID, display2.getDisplayId())));
+
+ // Assert than the overlay is not applied for contexts not associated with the above
+ // displays.
+ final Context context = getApplicationContext();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context);
+ final Display display3 =
+ mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDisplayContext(display3));
+
+ // Assert than the overlay is applied for contexts associated with the above displays.
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDisplayContext(display1));
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDisplayContext(display2));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_withTypesDisplayIdAndDeviceId_appliesOverlayWithConstraints()
+ throws Exception {
+ final Display display1 =
+ mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay();
+ final int deviceId1 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId();
+ enableOverlay(List.of(new OverlayConstraint(TYPE_DISPLAY_ID, display1.getDisplayId()),
+ new OverlayConstraint(TYPE_DEVICE_ID, deviceId1)));
+
+ // Assert than the overlay is not applied for contexts not associated with the above
+ // display or device.
+ final Context context = getApplicationContext();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context);
+ final Display display2 =
+ mVirtualDeviceRule.createManagedUnownedVirtualDisplay().getDisplay();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDisplayContext(display2));
+ final int deviceId2 = mVirtualDeviceRule.createManagedVirtualDevice().getDeviceId();
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, context.createDeviceContext(deviceId2));
+
+ // Assert than the overlay is applied for contexts associated with the above display or
+ // device.
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDisplayContext(display1));
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, context.createDeviceContext(deviceId1));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_withTypeDisplayId_appliesForActivityOnDisplay()
+ throws Exception {
+ final Display display =
+ mVirtualDeviceRule.createManagedUnownedVirtualDisplay(
+ VirtualDeviceRule.createTrustedVirtualDisplayConfigBuilder())
+ .getDisplay();
+ final Activity activityOnDefaultDisplay = mVirtualDeviceRule.startActivityOnDisplaySync(
+ DEFAULT_DISPLAY, Activity.class);
+ final Activity activityOnVirtualDisplay = mVirtualDeviceRule.startActivityOnDisplaySync(
+ display.getDisplayId(), Activity.class);
+
+ enableOverlay(List.of(new OverlayConstraint(TYPE_DISPLAY_ID, display.getDisplayId())));
+
+ // Assert than the overlay is not applied for any existing activity on the default display.
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, activityOnDefaultDisplay);
+ // Assert than the overlay is applied for any existing activity on the virtual display.
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, activityOnVirtualDisplay);
+
+ // Assert than the overlay is not applied for any new activity on the default display.
+ final Activity newActivityOnDefaultDisplay = mVirtualDeviceRule.startActivityOnDisplaySync(
+ DEFAULT_DISPLAY, Activity.class);
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, newActivityOnDefaultDisplay);
+ // Assert than the overlay is applied for any new activity on the virtual display.
+ final Activity newActivityOnVirtualDisplay = mVirtualDeviceRule.startActivityOnDisplaySync(
+ display.getDisplayId(), Activity.class);
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, newActivityOnVirtualDisplay);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_withTypeDeviceId_appliesForActivityOnDevice()
+ throws Exception {
+ final VirtualDeviceManager.VirtualDevice device =
+ mVirtualDeviceRule.createManagedVirtualDevice();
+ final Display display =
+ mVirtualDeviceRule.createManagedVirtualDisplay(device,
+ VirtualDeviceRule.createTrustedVirtualDisplayConfigBuilder())
+ .getDisplay();
+ final Activity activityOnDefaultDevice = mVirtualDeviceRule.startActivityOnDisplaySync(
+ DEFAULT_DISPLAY, Activity.class);
+ final Activity activityOnVirtualDevice = mVirtualDeviceRule.startActivityOnDisplaySync(
+ display.getDisplayId(), Activity.class);
+
+ enableOverlay(List.of(new OverlayConstraint(TYPE_DEVICE_ID, device.getDeviceId())));
+
+ // Assert than the overlay is not applied for any existing activity on the default device.
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, activityOnDefaultDevice);
+ // Assert than the overlay is applied for any existing activity on the virtual device.
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, activityOnVirtualDevice);
+
+ // Assert than the overlay is not applied for any new activity on the default device.
+ final Activity newActivityOnDefaultDevice = mVirtualDeviceRule.startActivityOnDisplaySync(
+ DEFAULT_DISPLAY, Activity.class);
+ ensureResourceValueStaysAt(RESOURCE_DEFAULT_VALUE, newActivityOnDefaultDevice);
+ // Assert than the overlay is applied for any new activity on the virtual device.
+ final Activity newActivityOnVirtualDevice = mVirtualDeviceRule.startActivityOnDisplaySync(
+ display.getDisplayId(), Activity.class);
+ waitForResourceValue(RESOURCE_OVERLAID_VALUE, newActivityOnVirtualDevice);
+ }
+
private FabricatedOverlay createFabricatedOverlay() {
- String packageName = getApplicationContext().getPackageName();
FabricatedOverlay fabricatedOverlay = new FabricatedOverlay.Builder(
- packageName, "testOverlay" /* name */, packageName)
+ mPackageName, "testOverlay" /* name */, mPackageName)
.build();
- fabricatedOverlay.setResourceValue("string/module_2_name" /* resourceName */,
- TypedValue.TYPE_STRING, "hello" /* value */, null /* configuration */);
+ fabricatedOverlay.setResourceValue(RESOURCE_NAME, TYPE_STRING, RESOURCE_OVERLAID_VALUE,
+ null /* configuration */);
return fabricatedOverlay;
}
@@ -183,6 +344,37 @@ public class OverlayConstraintsTests {
mOverlayIdentifier = fabricatedOverlay.getIdentifier();
}
+ private static void waitForResourceValue(final String expectedValue, Context context)
+ throws TimeoutException {
+ final long endTime = System.currentTimeMillis() + TIMEOUT_MILLIS;
+ final Resources resources = context.getResources();
+ final int resourceId = getResourceId(context);
+ String resourceValue = null;
+ while (System.currentTimeMillis() < endTime) {
+ resourceValue = resources.getString(resourceId);
+ if (Objects.equals(resourceValue, expectedValue)) {
+ return;
+ }
+ }
+ throw new TimeoutException("Timed out waiting for '" + RESOURCE_NAME + "' value to equal '"
+ + expectedValue + "': current value is '" + resourceValue + "'");
+ }
+
+ private static void ensureResourceValueStaysAt(final String expectedValue, Context context) {
+ final long endTime = System.currentTimeMillis() + TIMEOUT_MILLIS;
+ final Resources resources = context.getResources();
+ final int resourceId = getResourceId(context);
+ String resourceValue;
+ while (System.currentTimeMillis() < endTime) {
+ resourceValue = resources.getString(resourceId);
+ assertEquals(expectedValue, resourceValue);
+ }
+ }
+
+ private static int getResourceId(Context context) {
+ return context.getResources().getIdentifier(RESOURCE_NAME, "", context.getPackageName());
+ }
+
private static List<OverlayConstraint>[] getAllConstraintLists() {
return new List[]{
Collections.emptyList(),
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 3ef360a752f6..da14e451d656 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -95,8 +95,8 @@ import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
-import com.android.internal.infra.AndroidFuture;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
@@ -110,6 +110,7 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -149,6 +150,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected static final String MAIN_ACTIVITY_CLASS = "MainActivity";
protected static final String PIN_CONFIRM_ACTIVITY_CLASS = "PinConfirmActivity";
+ private byte[] mBaseState;
+ protected final SparseArray<byte[]> mUserStates = new SparseArray<>();
+
// public for mockito
public class BaseContext extends MockContext {
@Override
@@ -287,6 +291,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
final ServiceContext mContext;
IUidObserver mUidObserver;
+
public ShortcutServiceTestable(ServiceContext context, Looper looper) {
super(context, looper, /* onyForPackageManagerApis */ false);
mContext = context;
@@ -567,6 +572,58 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
// During tests, WTF is fatal.
fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th));
}
+
+ @Override
+ void injectSaveBaseState() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ saveBaseStateAsXml(baos);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ mBaseState = baos.toByteArray();
+ }
+
+ @Override
+ protected void injectLoadBaseState() {
+ if (mBaseState == null) {
+ return;
+ }
+ ByteArrayInputStream bais = new ByteArrayInputStream(mBaseState);
+ try {
+ loadBaseStateAsXml(bais);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void injectSaveUser(@UserIdInt int userId) {
+ synchronized (mServiceLock) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ saveUserInternalLocked(userId, baos, /* forBackup= */ false);
+ cleanupDanglingBitmapDirectoriesLocked(userId);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ mUserStates.put(userId, baos.toByteArray());
+ }
+ }
+
+ @Override
+ protected ShortcutUser injectLoadUserLocked(@UserIdInt int userId) {
+ final byte[] userState = mUserStates.get(userId);
+ if (userState == null) {
+ return null;
+ }
+ ByteArrayInputStream bais = new ByteArrayInputStream(userState);
+ try {
+ return loadUserInternal(userId, bais, /* forBackup= */ false);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
/** ShortcutManager with injection override methods. */
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index f536cae53e3a..58f762204c27 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -201,7 +201,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
- mService.saveBaseState();
+ mService.injectSaveBaseState();
dumpBaseStateFile();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 3b32701b5169..f5690b77d2fe 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -54,9 +54,7 @@ import androidx.test.filters.SmallTest;
import com.android.frameworks.servicestests.R;
import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
-import java.io.Writer;
import java.util.Locale;
/**
@@ -2358,12 +2356,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
* can still be read.
*/
public void testLoadLegacySavedFile() throws Exception {
- final File path = mService.getUserFile(USER_10).getBaseFile();
- path.getParentFile().mkdirs();
- try (Writer w = new FileWriter(path)) {
- w.write(readTestAsset("shortcut/shortcut_legacy_file.xml"));
- };
+ final String legacyFile = readTestAsset("shortcut/shortcut_legacy_file.xml");
+ mUserStates.put(USER_10, legacyFile.getBytes());
initService();
+
mService.handleUnlockUser(USER_10);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 0eb20eb22380..66d7611a29c6 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -32,6 +32,7 @@ android_test {
"androidx.test.rules",
"hamcrest-library",
"mockito-target-inline-minus-junit4",
+ "mockito-target-extended",
"platform-compat-test-rules",
"platform-test-annotations",
"platformprotosnano",
diff --git a/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java b/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
index 779fa1aa2f72..dbbe40fd42e6 100644
--- a/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
+++ b/services/tests/uiservicestests/src/android/app/NotificationManagerZenTest.java
@@ -80,7 +80,7 @@ public class NotificationManagerZenTest {
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @RequiresFlagsEnabled(Flags.FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualActivation() {
AutomaticZenRule ruleToCreate = createZenRule("rule");
String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
@@ -111,7 +111,7 @@ public class NotificationManagerZenTest {
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @RequiresFlagsEnabled(Flags.FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualDeactivation() {
AutomaticZenRule ruleToCreate = createZenRule("rule");
String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
@@ -145,7 +145,7 @@ public class NotificationManagerZenTest {
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @RequiresFlagsEnabled(Flags.FLAG_MODES_UI)
public void setAutomaticZenRuleState_respectsManuallyActivated() {
AutomaticZenRule ruleToCreate = createZenRule("rule");
String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
@@ -178,7 +178,7 @@ public class NotificationManagerZenTest {
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @RequiresFlagsEnabled(Flags.FLAG_MODES_UI)
public void setAutomaticZenRuleState_respectsManuallyDeactivated() {
AutomaticZenRule ruleToCreate = createZenRule("rule");
String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
@@ -212,7 +212,7 @@ public class NotificationManagerZenTest {
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @RequiresFlagsEnabled(Flags.FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualActivationFromApp() {
AutomaticZenRule ruleToCreate = createZenRule("rule");
String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
@@ -244,7 +244,7 @@ public class NotificationManagerZenTest {
}
@Test
- @RequiresFlagsEnabled({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @RequiresFlagsEnabled(Flags.FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualDeactivationFromApp() {
AutomaticZenRule ruleToCreate = createZenRule("rule");
String ruleId = mNotificationManager.addAutomaticZenRule(ruleToCreate);
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index c4b8599a483c..9930c9f07ed8 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -70,7 +70,6 @@ import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.Flags;
import android.app.IOnProjectionStateChangedListener;
import android.app.IUiModeManager;
import android.content.BroadcastReceiver;
@@ -91,7 +90,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.FakePermissionEnforcer;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
@@ -1508,13 +1506,11 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
public void testAttentionModeThemeOverlay_nightModeDisabled() throws RemoteException {
testAttentionModeThemeOverlay(false);
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
public void testAttentionModeThemeOverlay_nightModeEnabled() throws RemoteException {
testAttentionModeThemeOverlay(true);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index b3ec2153542a..c9d5241c57b7 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -30,6 +30,7 @@ import android.testing.TestableContext;
import androidx.test.InstrumentationRegistry;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
@@ -41,6 +42,7 @@ import org.mockito.MockitoAnnotations;
public class UiServiceTestCase {
@Mock protected PackageManagerInternal mPmi;
+ @Mock protected UserManagerInternal mUmi;
@Mock protected UriGrantsManagerInternal mUgmInternal;
protected static final String PKG_N_MR1 = "com.example.n_mr1";
@@ -92,6 +94,8 @@ public class UiServiceTestCase {
}
});
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, mUmi);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
when(mUgmInternal.checkGrantUriPermission(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 1890879da69d..5ce9a3e8d4d4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -47,7 +47,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.ColorDisplayManager;
import android.os.PowerManager;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenDeviceEffects;
import android.testing.TestableContext;
@@ -102,8 +101,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_appliesEffects() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.setShouldDimWallpaper(true)
@@ -119,7 +116,6 @@ public class DefaultDeviceEffectsApplierTest {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void apply_logsToZenLog() {
when(mPowerManager.isInteractive()).thenReturn(true);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
@@ -155,8 +151,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_removesEffects() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
ZenDeviceEffects previousEffects = new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.setShouldDimWallpaper(true)
@@ -180,8 +174,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_removesOnlyPreviouslyAppliedEffects() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
ZenDeviceEffects previousEffects = new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.build();
@@ -197,7 +189,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_missingSomeServices_okay() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mContext.addMockSystemService(ColorDisplayManager.class, null);
mContext.addMockSystemService(WallpaperManager.class, null);
mApplier = new DefaultDeviceEffectsApplier(mContext);
@@ -216,7 +207,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_disabledWallpaperService_dimWallpaperNotApplied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
WallpaperManager disabledWallpaperService = mock(WallpaperManager.class);
when(mWallpaperManager.isWallpaperSupported()).thenReturn(false);
mContext.addMockSystemService(WallpaperManager.class, disabledWallpaperService);
@@ -236,8 +226,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_someEffects_onlyThoseEffectsApplied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.setShouldDisplayGrayscale(true)
@@ -253,8 +241,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_onlyEffectDeltaApplied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
mApplier.apply(new ZenDeviceEffects.Builder().setShouldDimWallpaper(true).build(),
ORIGIN_USER_IN_SYSTEMUI);
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
@@ -272,7 +258,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_nightModeFromApp_appliedOnScreenOff() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
ArgumentCaptor<IntentFilter> intentFilterCaptor =
@@ -301,8 +286,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_nightModeWithScreenOff_appliedImmediately(
@TestParameter ZenChangeOrigin origin) {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
when(mPowerManager.isInteractive()).thenReturn(false);
mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
@@ -314,7 +297,6 @@ public class DefaultDeviceEffectsApplierTest {
}
@Test
- @EnableFlags({android.app.Flags.FLAG_MODES_API, android.app.Flags.FLAG_MODES_UI})
public void apply_nightModeWithScreenOnAndKeyguardShowing_appliedImmediately(
@TestParameter ZenChangeOrigin origin) {
@@ -334,8 +316,6 @@ public class DefaultDeviceEffectsApplierTest {
"{origin: ORIGIN_INIT}", "{origin: ORIGIN_INIT_USER}"})
public void apply_nightModeWithScreenOn_appliedImmediatelyBasedOnOrigin(
ZenChangeOrigin origin) {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
when(mPowerManager.isInteractive()).thenReturn(true);
mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
@@ -351,8 +331,6 @@ public class DefaultDeviceEffectsApplierTest {
"{origin: ORIGIN_SYSTEM}", "{origin: ORIGIN_UNKNOWN}"})
public void apply_nightModeWithScreenOn_willBeAppliedLaterBasedOnOrigin(
ZenChangeOrigin origin) {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
when(mPowerManager.isInteractive()).thenReturn(true);
mApplier.apply(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(),
@@ -367,8 +345,6 @@ public class DefaultDeviceEffectsApplierTest {
@Test
public void apply_servicesThrow_noCrash() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
doThrow(new RuntimeException()).when(mPowerManager)
.suppressAmbientDisplay(anyString(), anyBoolean());
doThrow(new RuntimeException()).when(mColorDisplayManager).setSaturationLevel(anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index e5c42082ab97..98440ecdad82 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -17,12 +17,17 @@ package com.android.server.notification;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_CURRENT;
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.notification.Flags.FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER;
+import static com.android.server.notification.Flags.managedServicesConcurrentMultiuser;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
import static com.android.server.notification.NotificationManagerService.privateSpaceFlagsEnabled;
@@ -66,7 +71,9 @@ import android.os.IInterface;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -83,6 +90,7 @@ import com.android.server.UiServiceTestCase;
import com.google.android.collect.Lists;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -105,6 +113,9 @@ import java.util.concurrent.CountDownLatch;
public class ManagedServicesTest extends UiServiceTestCase {
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private IPackageManager mIpm;
@Mock
@@ -155,6 +166,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
users.add(new UserInfo(11, "11", 0));
users.add(new UserInfo(12, "12", 0));
users.add(new UserInfo(13, "13", 0));
+ users.add(new UserInfo(99, "99", 0));
for (UserInfo user : users) {
when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
}
@@ -804,6 +816,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void rebindServices_onlyBindsExactMatchesIfComponent() throws Exception {
// If the primary and secondary lists contain component names, only those components within
// the package should be matched
@@ -841,6 +854,45 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void rebindServices_onlyBindsExactMatchesIfComponent_concurrent_multiUser()
+ throws Exception {
+ // If the primary and secondary lists contain component names, only those components within
+ // the package should be matched
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm,
+ ManagedServices.APPROVAL_BY_COMPONENT);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ packages.add("anotherPackage");
+ addExpectedServices(service, packages, 0);
+
+ // only 2 components are approved per package
+ mExpectedPrimaryComponentNames.clear();
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+ mExpectedSecondaryComponentNames.clear();
+ mExpectedSecondaryComponentNames.put(0, "anotherPackage/C1:anotherPackage/C2");
+
+ loadXml(service);
+ // verify the 2 components per package are enabled (bound)
+ verifyExpectedBoundEntries(service, true, 0);
+ verifyExpectedBoundEntries(service, false, 0);
+
+ // verify the last component per package is not enabled/we don't try to bind to it
+ for (String pkg : packages) {
+ ComponentName unapprovedAdditionalComponent =
+ ComponentName.unflattenFromString(pkg + "/C3");
+ assertFalse(
+ service.isComponentEnabledForUser(
+ unapprovedAdditionalComponent, 0));
+ verify(mIpm, never()).getServiceInfo(
+ eq(unapprovedAdditionalComponent), anyLong(), anyInt());
+ }
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void rebindServices_bindsEverythingInAPackage() throws Exception {
// If the primary and secondary lists contain packages, all components within those packages
// should be bound
@@ -866,6 +918,32 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void rebindServices_bindsEverythingInAPackage_concurrent_multiUser() throws Exception {
+ // If the primary and secondary lists contain packages, all components within those packages
+ // should be bound
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_PACKAGE);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ packages.add("packagea");
+ addExpectedServices(service, packages, 0);
+
+ // 2 approved packages
+ mExpectedPrimaryPackages.clear();
+ mExpectedPrimaryPackages.put(0, "package");
+ mExpectedSecondaryPackages.clear();
+ mExpectedSecondaryPackages.put(0, "packagea");
+
+ loadXml(service);
+
+ // verify the 3 components per package are enabled (bound)
+ verifyExpectedBoundEntries(service, true, 0);
+ verifyExpectedBoundEntries(service, false, 0);
+ }
+
+ @Test
public void reregisterService_checksAppIsApproved_pkg() throws Exception {
Context context = mock(Context.class);
PackageManager pm = mock(PackageManager.class);
@@ -1118,6 +1196,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void testUpgradeAppBindsNewServices() throws Exception {
// If the primary and secondary lists contain component names, only those components within
// the package should be matched
@@ -1159,6 +1238,49 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testUpgradeAppBindsNewServices_concurrent_multiUser() throws Exception {
+ // If the primary and secondary lists contain component names, only those components within
+ // the package should be matched
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm,
+ ManagedServices.APPROVAL_BY_PACKAGE);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ addExpectedServices(service, packages, 0);
+
+ // only 2 components are approved per package
+ mExpectedPrimaryComponentNames.clear();
+ mExpectedPrimaryPackages.clear();
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+ mExpectedSecondaryComponentNames.clear();
+ mExpectedSecondaryPackages.clear();
+
+ loadXml(service);
+
+ // new component expected
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2:package/C3");
+
+ service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+ // verify the 3 components per package are enabled (bound)
+ verifyExpectedBoundEntries(service, true, 0);
+
+ // verify the last component per package is not enabled/we don't try to bind to it
+ for (String pkg : packages) {
+ ComponentName unapprovedAdditionalComponent =
+ ComponentName.unflattenFromString(pkg + "/C3");
+ assertFalse(
+ service.isComponentEnabledForUser(
+ unapprovedAdditionalComponent, 0));
+ verify(mIpm, never()).getServiceInfo(
+ eq(unapprovedAdditionalComponent), anyLong(), anyInt());
+ }
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void testUpgradeAppNoPermissionNoRebind() throws Exception {
Context context = spy(getContext());
doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
@@ -1211,6 +1333,59 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testUpgradeAppNoPermissionNoRebind_concurrent_multiUser() throws Exception {
+ Context context = spy(getContext());
+ doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+ mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ addExpectedServices(service, packages, 0);
+
+ final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+ final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+ // Both components are approved initially
+ mExpectedPrimaryComponentNames.clear();
+ mExpectedPrimaryPackages.clear();
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+ mExpectedSecondaryComponentNames.clear();
+ mExpectedSecondaryPackages.clear();
+
+ loadXml(service);
+
+ //Component package/C1 loses bind permission
+ when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
+ (Answer<ServiceInfo>) invocation -> {
+ ComponentName invocationCn = invocation.getArgument(0);
+ if (invocationCn != null) {
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = invocationCn.getPackageName();
+ serviceInfo.name = invocationCn.getClassName();
+ if (invocationCn.equals(unapprovedComponent)) {
+ serviceInfo.permission = "none";
+ } else {
+ serviceInfo.permission = service.getConfig().bindPermission;
+ }
+ serviceInfo.metaData = null;
+ return serviceInfo;
+ }
+ return null;
+ }
+ );
+
+ // Trigger package update
+ service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+ assertFalse(service.isComponentEnabledForUser(unapprovedComponent, 0));
+ assertTrue(service.isComponentEnabledForUser(approvedComponent, 0));
+ }
+
+ @Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1517,6 +1692,201 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testPopulateComponentsToBindWithNonProfileUser() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ spyOn(service);
+
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+ ArraySet<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ approvedComponentsByUser.put(0, allowed0);
+ ArraySet<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ approvedComponentsByUser.put(10, allowed10);
+
+ int nonProfileUser = 99;
+ ArraySet<ComponentName> allowedForNonProfileUser = new ArraySet<>();
+ allowedForNonProfileUser.add(ComponentName.unflattenFromString("c/c"));
+ approvedComponentsByUser.put(nonProfileUser, allowedForNonProfileUser);
+
+ IntArray users = new IntArray();
+ users.add(nonProfileUser);
+ users.add(10);
+ users.add(0);
+
+ SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+ spyOn(service.mUmInternal);
+ when(service.mUmInternal.isVisibleBackgroundFullUser(nonProfileUser)).thenReturn(true);
+
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+
+ assertTrue(service.isComponentEnabledForUser(
+ ComponentName.unflattenFromString("a/a"), 0));
+ assertTrue(service.isComponentEnabledForPackage("a", 0));
+ assertTrue(service.isComponentEnabledForUser(
+ ComponentName.unflattenFromString("b/b"), 10));
+ assertTrue(service.isComponentEnabledForPackage("b", 0));
+ assertTrue(service.isComponentEnabledForPackage("b", 10));
+ assertTrue(service.isComponentEnabledForUser(
+ ComponentName.unflattenFromString("c/c"), nonProfileUser));
+ assertTrue(service.isComponentEnabledForPackage("c", nonProfileUser));
+ }
+
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testRebindService_profileUser() throws Exception {
+ final int profileUserId = 10;
+ when(mUserProfiles.isProfileUser(profileUserId, mContext)).thenReturn(true);
+ spyOn(mService);
+ ArgumentCaptor<IntArray> captor = ArgumentCaptor.forClass(
+ IntArray.class);
+ when(mService.allowRebindForParentUser()).thenReturn(true);
+
+ mService.rebindServices(false, profileUserId);
+
+ verify(mService).populateComponentsToBind(any(), captor.capture(), any());
+ assertTrue(captor.getValue().contains(0));
+ assertTrue(captor.getValue().contains(profileUserId));
+ }
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testRebindService_nonProfileUser() throws Exception {
+ final int userId = 99;
+ when(mUserProfiles.isProfileUser(userId, mContext)).thenReturn(false);
+ spyOn(mService);
+ ArgumentCaptor<IntArray> captor = ArgumentCaptor.forClass(
+ IntArray.class);
+ when(mService.allowRebindForParentUser()).thenReturn(true);
+
+ mService.rebindServices(false, userId);
+
+ verify(mService).populateComponentsToBind(any(), captor.capture(), any());
+ assertFalse(captor.getValue().contains(0));
+ assertTrue(captor.getValue().contains(userId));
+ }
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testRebindService_userAll() throws Exception {
+ final int userId = 99;
+ spyOn(mService);
+ spyOn(mService.mUmInternal);
+ when(mService.mUmInternal.isVisibleBackgroundFullUser(userId)).thenReturn(true);
+ ArgumentCaptor<IntArray> captor = ArgumentCaptor.forClass(
+ IntArray.class);
+ when(mService.allowRebindForParentUser()).thenReturn(true);
+
+ mService.rebindServices(false, USER_ALL);
+
+ verify(mService).populateComponentsToBind(any(), captor.capture(), any());
+ assertTrue(captor.getValue().contains(0));
+ assertTrue(captor.getValue().contains(userId));
+ }
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testOnUserStoppedWithVisibleBackgroundUser() throws Exception {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ spyOn(service);
+ int userId = 99;
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+ ArraySet<ComponentName> allowedForNonProfileUser = new ArraySet<>();
+ allowedForNonProfileUser.add(ComponentName.unflattenFromString("a/a"));
+ approvedComponentsByUser.put(userId, allowedForNonProfileUser);
+ IntArray users = new IntArray();
+ users.add(userId);
+ SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+ spyOn(service.mUmInternal);
+ when(service.mUmInternal.isVisibleBackgroundFullUser(userId)).thenReturn(true);
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+ assertTrue(service.isComponentEnabledForUser(
+ ComponentName.unflattenFromString("a/a"), userId));
+ assertTrue(service.isComponentEnabledForPackage("a", userId));
+
+ service.onUserStopped(userId);
+
+ assertFalse(service.isComponentEnabledForUser(
+ ComponentName.unflattenFromString("a/a"), userId));
+ assertFalse(service.isComponentEnabledForPackage("a", userId));
+ verify(service).unbindUserServices(eq(userId));
+ }
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testUnbindServicesImpl_serviceOfForegroundUser() throws Exception {
+ int switchingUserId = 10;
+ int userId = 99;
+
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ spyOn(service);
+ spyOn(service.mUmInternal);
+ when(service.mUmInternal.isVisibleBackgroundFullUser(userId)).thenReturn(false);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo serviceInfo = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), userId, false,
+ mock(ServiceConnection.class), 26, 34);
+
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(serviceInfo);
+
+ when(service.getRemovableConnectedServices()).thenReturn(removableBoundServices);
+ ArgumentCaptor<SparseArray<Set<ComponentName>>> captor = ArgumentCaptor.forClass(
+ SparseArray.class);
+
+ service.unbindServicesImpl(switchingUserId, true);
+
+ verify(service).unbindFromServices(captor.capture());
+
+ assertEquals(captor.getValue().size(), 1);
+ assertTrue(captor.getValue().indexOfKey(userId) != -1);
+ assertTrue(captor.getValue().get(userId).contains(
+ ComponentName.unflattenFromString("a/a")));
+ }
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testUnbindServicesImpl_serviceOfVisibleBackgroundUser() throws Exception {
+ int switchingUserId = 10;
+ int userId = 99;
+
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ spyOn(service);
+ spyOn(service.mUmInternal);
+ when(service.mUmInternal.isVisibleBackgroundFullUser(userId)).thenReturn(true);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo serviceInfo = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), userId,
+ false, mock(ServiceConnection.class), 26, 34);
+
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(serviceInfo);
+
+ when(service.getRemovableConnectedServices()).thenReturn(removableBoundServices);
+ ArgumentCaptor<SparseArray<Set<ComponentName>>> captor = ArgumentCaptor.forClass(
+ SparseArray.class);
+
+ service.unbindServicesImpl(switchingUserId, true);
+
+ verify(service).unbindFromServices(captor.capture());
+
+ assertEquals(captor.getValue().size(), 0);
+ }
+
@Test
public void testOnNullBinding() throws Exception {
Context context = mock(Context.class);
@@ -1681,6 +2051,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertFalse(service.isBound(cn, mZero.id));
assertFalse(service.isBound(cn, mTen.id));
}
+
@Test
public void testOnPackagesChanged_nullValuesPassed_noNullPointers() {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -2012,6 +2383,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isComponentEnabledForCurrentProfiles_isThreadSafe() throws InterruptedException {
for (UserInfo userInfo : mUm.getUsers()) {
mService.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", userInfo.id, true);
@@ -2024,6 +2396,20 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isComponentEnabledForUser_isThreadSafe() throws InterruptedException {
+ for (UserInfo userInfo : mUm.getUsers()) {
+ mService.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", userInfo.id, true);
+ }
+ testThreadSafety(() -> {
+ mService.rebindServices(false, 0);
+ assertThat(mService.isComponentEnabledForUser(
+ new ComponentName("pkg1", "cmp1"), 0)).isTrue();
+ }, 20, 30);
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isComponentEnabledForCurrentProfiles_profileUserId() {
final int profileUserId = 10;
when(mUserProfiles.isProfileUser(profileUserId, mContext)).thenReturn(true);
@@ -2037,6 +2423,24 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isComponentEnabledForUser_profileUserId() {
+ final int profileUserId = 10;
+ when(mUserProfiles.isProfileUser(profileUserId, mContext)).thenReturn(true);
+ spyOn(mService);
+ doReturn(USER_CURRENT).when(mService).resolveUserId(anyInt());
+
+ // Only approve for parent user (0)
+ mService.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", 0, true);
+
+ // Test that the component is enabled after calling rebindServices with profile userId (10)
+ mService.rebindServices(false, profileUserId);
+ assertThat(mService.isComponentEnabledForUser(
+ new ComponentName("pkg1", "cmp1"), profileUserId)).isTrue();
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isComponentEnabledForCurrentProfiles_profileUserId_NAS() {
final int profileUserId = 10;
when(mUserProfiles.isProfileUser(profileUserId, mContext)).thenReturn(true);
@@ -2054,6 +2458,25 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isComponentEnabledForUser_profileUserId_NAS() {
+ final int profileUserId = 10;
+ when(mUserProfiles.isProfileUser(profileUserId, mContext)).thenReturn(true);
+ // Do not rebind for parent users (NAS use-case)
+ ManagedServices service = spy(mService);
+ when(service.allowRebindForParentUser()).thenReturn(false);
+ doReturn(USER_CURRENT).when(service).resolveUserId(anyInt());
+
+ // Only approve for parent user (0)
+ service.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", 0, true);
+
+ // Test that the component is disabled after calling rebindServices with profile userId (10)
+ service.rebindServices(false, profileUserId);
+ assertThat(service.isComponentEnabledForUser(
+ new ComponentName("pkg1", "cmp1"), profileUserId)).isFalse();
+ }
+
+ @Test
@EnableFlags(FLAG_LIFETIME_EXTENSION_REFACTOR)
public void testManagedServiceInfoIsSystemUi() {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
@@ -2069,6 +2492,48 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertThat(service0.isSystemUi()).isFalse();
}
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testUserMatchesAndEnabled_profileUser() throws Exception {
+ int currentUserId = 10;
+ int profileUserId = 11;
+
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ManagedServices.ManagedServiceInfo listener = spy(service.new ManagedServiceInfo(
+ mock(IInterface.class), ComponentName.unflattenFromString("a/a"), currentUserId,
+ false, mock(ServiceConnection.class), 26, 34));
+
+ doReturn(currentUserId).when(service.mUmInternal).getProfileParentId(profileUserId);
+ doReturn(currentUserId).when(service.mUmInternal).getProfileParentId(currentUserId);
+ doReturn(true).when(listener).isEnabledForUser();
+ doReturn(true).when(mUserProfiles).isCurrentProfile(anyInt());
+
+ assertThat(listener.enabledAndUserMatches(profileUserId)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void testUserMatchesAndDisabled_visibleBackgroudUser() throws Exception {
+ int currentUserId = 10;
+ int profileUserId = 11;
+ int visibleBackgroundUserId = 12;
+
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ManagedServices.ManagedServiceInfo listener = spy(service.new ManagedServiceInfo(
+ mock(IInterface.class), ComponentName.unflattenFromString("a/a"), profileUserId,
+ false, mock(ServiceConnection.class), 26, 34));
+
+ doReturn(currentUserId).when(service.mUmInternal).getProfileParentId(profileUserId);
+ doReturn(currentUserId).when(service.mUmInternal).getProfileParentId(currentUserId);
+ doReturn(visibleBackgroundUserId).when(service.mUmInternal)
+ .getProfileParentId(visibleBackgroundUserId);
+ doReturn(true).when(listener).isEnabledForUser();
+
+ assertThat(listener.enabledAndUserMatches(visibleBackgroundUserId)).isFalse();
+ }
+
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
throws RemoteException {
@@ -2247,26 +2712,47 @@ public class ManagedServicesTest extends UiServiceTestCase {
private void verifyExpectedBoundEntries(ManagedServices service, boolean primary)
throws Exception {
+ verifyExpectedBoundEntries(service, primary, UserHandle.USER_CURRENT);
+ }
+
+ private void verifyExpectedBoundEntries(ManagedServices service, boolean primary,
+ int targetUserId) throws Exception {
ArrayMap<Integer, String> verifyMap = primary ? mExpectedPrimary.get(service.mApprovalLevel)
: mExpectedSecondary.get(service.mApprovalLevel);
for (int userId : verifyMap.keySet()) {
for (String packageOrComponent : verifyMap.get(userId).split(":")) {
if (!TextUtils.isEmpty(packageOrComponent)) {
if (service.mApprovalLevel == APPROVAL_BY_PACKAGE) {
- assertTrue(packageOrComponent,
- service.isComponentEnabledForPackage(packageOrComponent));
+ if (managedServicesConcurrentMultiuser()) {
+ assertTrue(packageOrComponent,
+ service.isComponentEnabledForPackage(packageOrComponent,
+ targetUserId));
+ } else {
+ assertTrue(packageOrComponent,
+ service.isComponentEnabledForPackage(packageOrComponent));
+ }
for (int i = 1; i <= 3; i++) {
ComponentName componentName = ComponentName.unflattenFromString(
packageOrComponent +"/C" + i);
- assertTrue(service.isComponentEnabledForCurrentProfiles(
- componentName));
+ if (managedServicesConcurrentMultiuser()) {
+ assertTrue(service.isComponentEnabledForUser(
+ componentName, targetUserId));
+ } else {
+ assertTrue(service.isComponentEnabledForCurrentProfiles(
+ componentName));
+ }
verify(mIpm, times(1)).getServiceInfo(
eq(componentName), anyLong(), anyInt());
}
} else {
ComponentName componentName =
ComponentName.unflattenFromString(packageOrComponent);
- assertTrue(service.isComponentEnabledForCurrentProfiles(componentName));
+ if (managedServicesConcurrentMultiuser()) {
+ assertTrue(service.isComponentEnabledForUser(componentName,
+ targetUserId));
+ } else {
+ assertTrue(service.isComponentEnabledForCurrentProfiles(componentName));
+ }
verify(mIpm, times(1)).getServiceInfo(
eq(componentName), anyLong(), anyInt());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 415e3accfa39..37ab541f12da 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -140,6 +140,7 @@ import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
+import static com.android.server.notification.Flags.FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER;
import static com.android.server.notification.Flags.FLAG_REJECT_OLD_NOTIFICATIONS;
import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
@@ -867,7 +868,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
&& filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
mPackageIntentReceiver = broadcastReceivers.get(i);
}
- if (filter.hasAction(Intent.ACTION_USER_SWITCHED)
+ if (filter.hasAction(Intent.ACTION_USER_STOPPED)
+ || filter.hasAction(Intent.ACTION_USER_SWITCHED)
|| filter.hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)
|| filter.hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
// There may be multiple receivers, get the NMS one
@@ -11028,7 +11030,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeManagedCanBeUsedByDeviceOwners() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.setCallerIsNormalPackage();
@@ -11046,20 +11047,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeManagedCanBeUsedBySystem() throws Exception {
addAutomaticZenRule_restrictedRuleTypeCanBeUsedBySystem(AutomaticZenRule.TYPE_MANAGED);
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeManagedCannotBeUsedByRegularApps() throws Exception {
addAutomaticZenRule_restrictedRuleTypeCannotBeUsedByRegularApps(
AutomaticZenRule.TYPE_MANAGED);
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeBedtimeCanBeUsedByWellbeing() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.setCallerIsNormalPackage();
@@ -11082,7 +11080,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeBedtimeCanBeUsedBySystem() throws Exception {
reset(mPackageManagerInternal);
when(mPackageManagerInternal.isSameApp(eq(mPkg), eq(mUid), anyInt())).thenReturn(true);
@@ -11090,7 +11087,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testAddAutomaticZenRule_typeBedtimeCannotBeUsedByRegularApps() throws Exception {
reset(mPackageManagerInternal);
when(mPackageManagerInternal.isSameApp(eq(mPkg), eq(mUid), anyInt())).thenReturn(true);
@@ -11133,7 +11129,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void addAutomaticZenRule_fromUser_mappedToOriginUser() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.isSystemUid = true;
@@ -11145,7 +11140,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void addAutomaticZenRule_fromSystemNotUser_mappedToOriginSystem() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.isSystemUid = true;
@@ -11157,7 +11151,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void addAutomaticZenRule_fromApp_mappedToOriginApp() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.setCallerIsNormalPackage();
@@ -11169,7 +11162,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void addAutomaticZenRule_fromAppFromUser_blocked() throws Exception {
setUpMockZenTest();
mService.setCallerIsNormalPackage();
@@ -11179,7 +11171,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void updateAutomaticZenRule_fromUserFromSystem_allowed() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.isSystemUid = true;
@@ -11191,7 +11182,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void updateAutomaticZenRule_fromUserFromApp_blocked() throws Exception {
setUpMockZenTest();
mService.setCallerIsNormalPackage();
@@ -11201,7 +11191,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void removeAutomaticZenRule_fromUserFromSystem_allowed() throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
mService.isSystemUid = true;
@@ -11213,7 +11202,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void removeAutomaticZenRule_fromUserFromApp_blocked() throws Exception {
setUpMockZenTest();
mService.setCallerIsNormalPackage();
@@ -11223,7 +11211,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setAutomaticZenRuleState_fromAppWithConditionFromUser_originUserInApp()
throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
@@ -11238,7 +11225,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setAutomaticZenRuleState_fromAppWithConditionNotFromUser_originApp()
throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
@@ -11253,7 +11239,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setAutomaticZenRuleState_fromSystemWithConditionFromUser_originUserInSystemUi()
throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
@@ -11267,7 +11252,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
eq(ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI), anyInt());
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setAutomaticZenRuleState_fromSystemWithConditionNotFromUser_originSystem()
throws Exception {
ZenModeHelper zenModeHelper = setUpMockZenTest();
@@ -11438,7 +11422,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void onAutomaticRuleStatusChanged_sendsBroadcastToRuleOwner() throws Exception {
mService.mZenModeHelper.getCallbacks().forEach(c -> c.onAutomaticRuleStatusChanged(
mUserId, "rule.owner.pkg", "rule_id", AUTOMATIC_RULE_STATUS_ACTIVATED));
@@ -16302,7 +16285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
InOrder inOrder = inOrder(mPreferencesHelper, mService.mZenModeHelper);
inOrder.verify(mService.mZenModeHelper).onUserSwitched(eq(20));
- inOrder.verify(mPreferencesHelper).syncChannelsBypassingDnd();
+ inOrder.verify(mPreferencesHelper).syncHasPriorityChannels();
inOrder.verifyNoMoreInteractions();
}
@@ -16318,11 +16301,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
InOrder inOrder = inOrder(mPreferencesHelper, mService.mZenModeHelper);
inOrder.verify(mService.mZenModeHelper).onUserSwitched(eq(20));
- inOrder.verify(mPreferencesHelper).syncChannelsBypassingDnd();
+ inOrder.verify(mPreferencesHelper).syncHasPriorityChannels();
inOrder.verifyNoMoreInteractions();
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void onUserStopped_callBackToListeners() {
+ Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, 20);
+
+ mUserIntentReceiver.onReceive(mContext, intent);
+
+ verify(mConditionProviders).onUserStopped(eq(20));
+ verify(mListeners).onUserStopped(eq(20));
+ verify(mAssistants).onUserStopped(eq(20));
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_invalidPackage() throws Exception {
final String notReal = "NOT REAL";
final var checker = mService.permissionChecker;
@@ -16339,6 +16336,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_invalidPackage_concurrent_multiUser()
+ throws Exception {
+ final String notReal = "NOT REAL";
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(notReal), anyInt())).thenThrow(
+ PackageManager.NameNotFoundException.class);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(notReal)).isFalse();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(notReal), anyInt());
+ verify(checker, never()).check(any(), anyInt(), anyInt(), anyBoolean());
+ verify(mConditionProviders, never()).isPackageOrComponentAllowed(eq(notReal), anyInt());
+ verify(mListeners, never()).isComponentEnabledForPackage(any(), anyInt());
+ verify(mDevicePolicyManager, never()).isActiveDeviceOwner(anyInt());
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_hasPermission() throws Exception {
final String packageName = "target";
final int uid = 123;
@@ -16357,6 +16373,27 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_hasPermission_concurrent_multiUser()
+ throws Exception {
+ final String packageName = "target";
+ final int uid = 123;
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
+ when(checker.check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(packageName)).isTrue();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(packageName), anyInt());
+ verify(checker).check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true);
+ verify(mConditionProviders, never()).isPackageOrComponentAllowed(eq(packageName), anyInt());
+ verify(mListeners, never()).isComponentEnabledForPackage(any(), anyInt());
+ verify(mDevicePolicyManager, never()).isActiveDeviceOwner(anyInt());
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_isPackageAllowed() throws Exception {
final String packageName = "target";
final int uid = 123;
@@ -16375,6 +16412,27 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_isPackageAllowed_concurrent_multiUser()
+ throws Exception {
+ final String packageName = "target";
+ final int uid = 123;
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
+ when(mConditionProviders.isPackageOrComponentAllowed(eq(packageName), anyInt()))
+ .thenReturn(true);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(packageName)).isTrue();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(packageName), anyInt());
+ verify(checker).check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true);
+ verify(mConditionProviders).isPackageOrComponentAllowed(eq(packageName), anyInt());
+ verify(mListeners, never()).isComponentEnabledForPackage(any(), anyInt());
+ verify(mDevicePolicyManager, never()).isActiveDeviceOwner(anyInt());
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_isComponentEnabled() throws Exception {
final String packageName = "target";
final int uid = 123;
@@ -16392,6 +16450,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_isComponentEnabled_concurrent_multiUser()
+ throws Exception {
+ final String packageName = "target";
+ final int uid = 123;
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
+ when(mListeners.isComponentEnabledForPackage(packageName, mUserId)).thenReturn(true);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(packageName)).isTrue();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(packageName), anyInt());
+ verify(checker).check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true);
+ verify(mConditionProviders).isPackageOrComponentAllowed(eq(packageName), anyInt());
+ verify(mListeners).isComponentEnabledForPackage(packageName, mUserId);
+ verify(mDevicePolicyManager, never()).isActiveDeviceOwner(anyInt());
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_isDeviceOwner() throws Exception {
final String packageName = "target";
final int uid = 123;
@@ -16408,10 +16486,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mDevicePolicyManager).isActiveDeviceOwner(uid);
}
+ @Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_isDeviceOwner_concurrent_multiUser()
+ throws Exception {
+ final String packageName = "target";
+ final int uid = 123;
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
+ when(mDevicePolicyManager.isActiveDeviceOwner(uid)).thenReturn(true);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(packageName)).isTrue();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(packageName), anyInt());
+ verify(checker).check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true);
+ verify(mConditionProviders).isPackageOrComponentAllowed(eq(packageName), anyInt());
+ verify(mListeners).isComponentEnabledForPackage(packageName, mUserId);
+ verify(mDevicePolicyManager).isActiveDeviceOwner(uid);
+ }
+
/**
* b/292163859
*/
@Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_callerIsDeviceOwner() throws Exception {
final String packageName = "target";
final int uid = 123;
@@ -16430,7 +16528,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mDevicePolicyManager, never()).isActiveDeviceOwner(callingUid);
}
+ /**
+ * b/292163859
+ */
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_callerIsDeviceOwner_concurrent_multiUser()
+ throws Exception {
+ final String packageName = "target";
+ final int uid = 123;
+ final int callingUid = Binder.getCallingUid();
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
+ when(mDevicePolicyManager.isActiveDeviceOwner(callingUid)).thenReturn(true);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(packageName)).isFalse();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(packageName), anyInt());
+ verify(checker).check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true);
+ verify(mConditionProviders).isPackageOrComponentAllowed(eq(packageName), anyInt());
+ verify(mListeners).isComponentEnabledForPackage(packageName, mUserId);
+ verify(mDevicePolicyManager).isActiveDeviceOwner(uid);
+ verify(mDevicePolicyManager, never()).isActiveDeviceOwner(callingUid);
+ }
+
+ @Test
+ @DisableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
public void isNotificationPolicyAccessGranted_notGranted() throws Exception {
final String packageName = "target";
final int uid = 123;
@@ -16447,6 +16570,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_MANAGED_SERVICES_CONCURRENT_MULTIUSER)
+ public void isNotificationPolicyAccessGranted_notGranted_concurrent_multiUser()
+ throws Exception {
+ final String packageName = "target";
+ final int uid = 123;
+ final var checker = mService.permissionChecker;
+
+ when(mPackageManagerClient.getPackageUidAsUser(eq(packageName), anyInt())).thenReturn(uid);
+
+ assertThat(mBinderService.isNotificationPolicyAccessGranted(packageName)).isFalse();
+ verify(mPackageManagerClient).getPackageUidAsUser(eq(packageName), anyInt());
+ verify(checker).check(android.Manifest.permission.MANAGE_NOTIFICATIONS, uid, -1, true);
+ verify(mConditionProviders).isPackageOrComponentAllowed(eq(packageName), anyInt());
+ verify(mListeners).isComponentEnabledForPackage(packageName, mUserId);
+ verify(mDevicePolicyManager).isActiveDeviceOwner(uid);
+ }
+
+ @Test
public void testResetDefaultDnd() {
TestableNotificationManagerService service = spy(mService);
UserInfo user = new UserInfo(0, "owner", 0);
@@ -16481,7 +16622,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setDeviceEffectsApplier_succeeds() throws Exception {
initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY);
@@ -16492,7 +16632,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setDeviceEffectsApplier_tooLate_throws() throws Exception {
initNMS(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
@@ -16501,7 +16640,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
public void setDeviceEffectsApplier_calledTwice_throws() throws Exception {
initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY);
@@ -16513,7 +16651,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void setNotificationPolicy_mappedToImplicitRule() throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenHelper;
@@ -16530,7 +16667,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void setNotificationPolicy_systemCaller_setsGlobalPolicy() throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
@@ -16570,7 +16706,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private void setNotificationPolicy_dependingOnCompanionAppDevice_maySetGlobalPolicy(
@AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
@@ -16597,7 +16732,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@DisableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void setNotificationPolicy_withoutCompat_setsGlobalPolicy() throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
@@ -16613,7 +16747,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void getNotificationPolicy_mappedFromImplicitRule() throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenHelper;
@@ -16628,7 +16761,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void setInterruptionFilter_mappedToImplicitRule() throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenHelper;
@@ -16644,7 +16776,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void setInterruptionFilter_systemCaller_setsGlobalPolicy() throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mService.setCallerIsNormalPackage();
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
@@ -16683,7 +16814,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private void setInterruptionFilter_dependingOnCompanionAppDevice_maySetGlobalZen(
@AssociationRequest.DeviceProfile String deviceProfile, boolean canSetGlobalPolicy)
throws RemoteException {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
mService.mZenModeHelper = zenModeHelper;
mService.setCallerIsNormalPackage();
@@ -16708,7 +16838,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void requestInterruptionFilterFromListener_fromApp_doesNotSetGlobalZen()
throws Exception {
@@ -16726,7 +16855,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void requestInterruptionFilterFromListener_fromSystem_setsGlobalZen()
throws Exception {
@@ -16745,24 +16873,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(android.app.Flags.FLAG_MODES_API)
- public void requestInterruptionFilterFromListener_flagOff_callsRequestFromListener()
- throws Exception {
- mService.setCallerIsNormalPackage();
- mService.mZenModeHelper = mock(ZenModeHelper.class);
- ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
- when(mListeners.checkServiceTokenLocked(any())).thenReturn(info);
- info.component = new ComponentName("pkg", "cls");
-
- mBinderService.requestInterruptionFilterFromListener(mock(INotificationListener.class),
- INTERRUPTION_FILTER_PRIORITY);
-
- verify(mService.mZenModeHelper).requestFromListener(eq(info.component),
- eq(INTERRUPTION_FILTER_PRIORITY), eq(mUid), /* fromSystemOrSystemUi= */ eq(false));
- }
-
- @Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
setUpRealZenTest();
@@ -16788,7 +16898,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
@EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
setUpRealZenTest();
@@ -16814,7 +16923,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({android.app.Flags.FLAG_MODES_API, android.app.Flags.FLAG_MODES_UI})
+ @EnableFlags(android.app.Flags.FLAG_MODES_UI)
public void setNotificationPolicy_fromSystemApp_appliesPriorityChannelsAllowed()
throws Exception {
setUpRealZenTest();
@@ -16844,7 +16953,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({android.app.Flags.FLAG_MODES_API, android.app.Flags.FLAG_MODES_UI})
+ @EnableFlags(android.app.Flags.FLAG_MODES_UI)
@DisableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
public void setNotificationPolicy_fromRegularAppThatCanModifyPolicy_ignoresState()
throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 640de174ba20..67e85ff2445d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -363,7 +363,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
.when(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+ NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(),
anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT);
@@ -2733,7 +2733,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -2748,7 +2748,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
- assertTrue(mHelper.areChannelsBypassingDnd());
+ assertTrue(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(true));
@@ -2760,7 +2760,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// delete channels
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
- assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+ assertTrue(mHelper.hasPriorityChannels()); // channel2 can still bypass DND
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -2770,7 +2770,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
resetZenModeHelper();
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -2792,7 +2792,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -2807,7 +2807,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.createNotificationChannel(PKG_N_MR1, uid, update, true, true,
uid, false);
- assertTrue(mHelper.areChannelsBypassingDnd());
+ assertTrue(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(true));
@@ -2829,7 +2829,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -2844,7 +2844,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
- assertTrue(mHelper.areChannelsBypassingDnd());
+ assertTrue(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(true));
@@ -2856,7 +2856,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// delete channels
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId(), uid, false);
- assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+ assertTrue(mHelper.hasPriorityChannels()); // channel2 can still bypass DND
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -2866,7 +2866,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
resetZenModeHelper();
mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId(), uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -2884,9 +2884,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start in a 'allowed to bypass dnd state'
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+ NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
- mHelper.syncChannelsBypassingDnd();
+ mHelper.syncHasPriorityChannels();
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2899,7 +2899,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -2917,9 +2917,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start in a 'allowed to bypass dnd state'
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+ NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
- mHelper.syncChannelsBypassingDnd();
+ mHelper.syncHasPriorityChannels();
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2927,7 +2927,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -2945,9 +2945,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start in a 'allowed to bypass dnd state'
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+ NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
- mHelper.syncChannelsBypassingDnd();
+ mHelper.syncHasPriorityChannels();
// create notification channel that can bypass dnd, but app is blocked
// expected result: areChannelsBypassingDnd = false
@@ -2955,7 +2955,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setBypassDnd(true);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -2977,7 +2977,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false,
uid, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -2990,7 +2990,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// expected result: areChannelsBypassingDnd = true
channel.setBypassDnd(true);
mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
- assertTrue(mHelper.areChannelsBypassingDnd());
+ assertTrue(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(true));
@@ -3004,7 +3004,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// expected result: areChannelsBypassingDnd = false
channel.setBypassDnd(false);
mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true, SYSTEM_UID, true);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -3020,10 +3020,10 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start notification policy off with mAreChannelsBypassingDnd = true, but
// RankingHelper should change to false
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+ NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
- mHelper.syncChannelsBypassingDnd();
- assertFalse(mHelper.areChannelsBypassingDnd());
+ mHelper.syncHasPriorityChannels();
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, times(1)).updateHasPriorityChannels(eq(UserHandle.CURRENT),
eq(false));
@@ -3039,7 +3039,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// start notification policy off with mAreChannelsBypassingDnd = false
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
- assertFalse(mHelper.areChannelsBypassingDnd());
+ assertFalse(mHelper.hasPriorityChannels());
if (android.app.Flags.modesUi()) {
verify(mMockZenModeHelper, never()).updateHasPriorityChannels(any(), anyBoolean());
} else {
@@ -3050,7 +3050,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void syncChannelsBypassingDnd_includesProfilesOfCurrentUser() throws Exception {
+ public void syncHasPriorityChannels_includesProfilesOfCurrentUser() throws Exception {
when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0, 10}));
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
ApplicationInfo appInfo = new ApplicationInfo();
@@ -3067,13 +3067,13 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
false, false, Process.SYSTEM_UID, true);
- mHelper.syncChannelsBypassingDnd();
+ mHelper.syncHasPriorityChannels();
- assertThat(mHelper.areChannelsBypassingDnd()).isTrue();
+ assertThat(mHelper.hasPriorityChannels()).isTrue();
}
@Test
- public void syncChannelsBypassingDnd_excludesOtherUsers() throws Exception {
+ public void syncHasPriorityChannels_excludesOtherUsers() throws Exception {
when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
ApplicationInfo appInfo = new ApplicationInfo();
@@ -3090,9 +3090,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
false, false, Process.SYSTEM_UID, true);
- mHelper.syncChannelsBypassingDnd();
+ mHelper.syncHasPriorityChannels();
- assertThat(mHelper.areChannelsBypassingDnd()).isFalse();
+ assertThat(mHelper.hasPriorityChannels()).isFalse();
}
@Test
@@ -4495,6 +4495,27 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testBubblePreference_sameVersionWithSAWPermission() throws Exception {
+ when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+ anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+ final String xml = "<ranking version=\"4\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+ + "<channel id=\"someId\" name=\"hi\""
+ + " importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
public void testBubblePreference_upgradeWithSAWThenUserOverride() throws Exception {
when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index f90034614383..ec428d506e7b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -17,9 +17,10 @@ package com.android.server.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
-
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -29,7 +30,6 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.app.Flags;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -40,7 +40,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
-import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
@@ -67,7 +66,6 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -154,7 +152,7 @@ public class RankingHelperTest extends UiServiceTestCase {
.thenReturn(SOUND_URI);
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+ NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS, 0);
when(mMockZenModeHelper.getNotificationPolicy(any())).thenReturn(mTestNotificationPolicy);
mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
mUsageStats, new String[] {ImportanceExtractor.class.getName()},
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
index 75552bc433c5..f3813437a9c5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -20,10 +20,7 @@ import static android.service.notification.ZenAdapters.notificationPolicyToZenPo
import static com.google.common.truth.Truth.assertThat;
-import android.app.Flags;
import android.app.NotificationManager.Policy;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
@@ -137,8 +134,7 @@ public class ZenAdaptersTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
- public void notificationPolicyToZenPolicy_modesApi_priorityChannels() {
+ public void notificationPolicyToZenPolicy_priorityChannels() {
Policy policy = new Policy(0, 0, 0, 0,
Policy.policyState(false, true), 0);
@@ -151,20 +147,4 @@ public class ZenAdaptersTest extends UiServiceTestCase {
assertThat(zenPolicyNotAllowed.getPriorityChannelsAllowed()).isEqualTo(
ZenPolicy.STATE_DISALLOW);
}
-
- @Test
- @DisableFlags(Flags.FLAG_MODES_API)
- public void notificationPolicyToZenPolicy_noModesApi_priorityChannelsUnset() {
- Policy policy = new Policy(0, 0, 0, 0,
- Policy.policyState(false, true), 0);
-
- ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
- assertThat(zenPolicy.getPriorityChannelsAllowed()).isEqualTo(ZenPolicy.STATE_UNSET);
-
- Policy notAllowed = new Policy(0, 0, 0, 0,
- Policy.policyState(false, false), 0);
- ZenPolicy zenPolicyNotAllowed = notificationPolicyToZenPolicy(notAllowed);
- assertThat(zenPolicyNotAllowed.getPriorityChannelsAllowed()).isEqualTo(
- ZenPolicy.STATE_UNSET);
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
index af911e811e5e..9a2b748a9bcc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
-import android.app.Flags;
import android.os.Parcel;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenDeviceEffects;
@@ -31,7 +30,6 @@ import com.android.server.UiServiceTestCase;
import com.google.common.collect.ImmutableSet;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,11 +40,6 @@ public class ZenDeviceEffectsTest extends UiServiceTestCase {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- @Before
- public final void setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
- }
-
@Test
public void builder() {
ZenDeviceEffects deviceEffects =
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index b42a6a5a7382..67efb9e76692 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -174,7 +174,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
}
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
- config.areChannelsBypassingDnd = true;
+ config.hasPriorityChannels = true;
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
if (Flags.modesUi()) {
@@ -187,7 +187,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
- config.areChannelsBypassingDnd = false;
+ config.hasPriorityChannels = false;
if (Flags.modesUi()) {
config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
.allowPriorityChannels(false)
@@ -417,7 +417,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
- config.areChannelsBypassingDnd = true;
+ config.hasPriorityChannels = true;
if (Flags.modesUi()) {
config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
.allowPriorityChannels(true)
@@ -429,7 +429,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
- config.areChannelsBypassingDnd = false;
+ config.hasPriorityChannels = false;
if (Flags.modesUi()) {
config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
.allowPriorityChannels(false)
@@ -488,33 +488,33 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.zenPolicy = null;
rule.zenDeviceEffects = null;
- assertThat(rule.canBeUpdatedByApp()).isTrue();
+ assertThat(rule.isUserModified()).isFalse();
rule.userModifiedFields = 1;
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
}
@Test
public void testCanBeUpdatedByApp_policyModified() throws Exception {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.zenPolicy = new ZenPolicy();
- assertThat(rule.canBeUpdatedByApp()).isTrue();
+ assertThat(rule.isUserModified()).isFalse();
rule.zenPolicyUserModifiedFields = 1;
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
}
@Test
public void testCanBeUpdatedByApp_deviceEffectsModified() throws Exception {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.zenDeviceEffects = new ZenDeviceEffects.Builder().build();
- assertThat(rule.canBeUpdatedByApp()).isTrue();
+ assertThat(rule.isUserModified()).isFalse();
rule.zenDeviceEffectsUserModifiedFields = 1;
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
}
@Test
@@ -548,7 +548,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.creationTime = 123;
rule.id = "id";
rule.zenMode = INTERRUPTION_FILTER;
- rule.modified = true;
rule.name = NAME;
rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
@@ -564,6 +563,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
if (Flags.modesUi()) {
rule.disabledOrigin = ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+ if (Flags.modesCleanupImplicit()) {
+ rule.lastActivation = Instant.ofEpochMilli(456);
+ }
}
config.automaticRules.put(rule.id, rule);
@@ -585,7 +587,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.condition, ruleActual.condition);
assertEquals(rule.enabled, ruleActual.enabled);
assertEquals(rule.creationTime, ruleActual.creationTime);
- assertEquals(rule.modified, ruleActual.modified);
assertEquals(rule.conditionId, ruleActual.conditionId);
assertEquals(rule.name, ruleActual.name);
assertEquals(rule.zenMode, ruleActual.zenMode);
@@ -602,6 +603,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.deletionInstant, ruleActual.deletionInstant);
if (Flags.modesUi()) {
assertEquals(rule.disabledOrigin, ruleActual.disabledOrigin);
+ if (Flags.modesCleanupImplicit()) {
+ assertEquals(rule.lastActivation, ruleActual.lastActivation);
+ }
}
if (Flags.backupRestoreLogging()) {
verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, 2);
@@ -620,7 +624,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.creationTime = 123;
rule.id = "id";
rule.zenMode = INTERRUPTION_FILTER;
- rule.modified = true;
rule.name = NAME;
rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
@@ -636,6 +639,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
if (Flags.modesUi()) {
rule.disabledOrigin = ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+ if (Flags.modesCleanupImplicit()) {
+ rule.lastActivation = Instant.ofEpochMilli(789);
+ }
}
Parcel parcel = Parcel.obtain();
@@ -651,7 +657,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.condition, parceled.condition);
assertEquals(rule.enabled, parceled.enabled);
assertEquals(rule.creationTime, parceled.creationTime);
- assertEquals(rule.modified, parceled.modified);
assertEquals(rule.conditionId, parceled.conditionId);
assertEquals(rule.name, parceled.name);
assertEquals(rule.zenMode, parceled.zenMode);
@@ -668,6 +673,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.deletionInstant, parceled.deletionInstant);
if (Flags.modesUi()) {
assertEquals(rule.disabledOrigin, parceled.disabledOrigin);
+ if (Flags.modesCleanupImplicit()) {
+ assertEquals(rule.lastActivation, parceled.lastActivation);
+ }
}
assertEquals(rule, parceled);
@@ -685,7 +693,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.creationTime = 123;
rule.id = "id";
rule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
- rule.modified = true;
rule.name = "name";
rule.snoozing = true;
rule.pkg = "b";
@@ -705,7 +712,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.condition, fromXml.condition);
assertEquals(rule.enabled, fromXml.enabled);
assertEquals(rule.creationTime, fromXml.creationTime);
- assertEquals(rule.modified, fromXml.modified);
assertEquals(rule.conditionId, fromXml.conditionId);
assertEquals(rule.name, fromXml.name);
assertEquals(rule.zenMode, fromXml.zenMode);
@@ -721,7 +727,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.enabled = ENABLED;
rule.id = "id";
rule.zenMode = INTERRUPTION_FILTER;
- rule.modified = true;
rule.name = NAME;
rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
@@ -753,6 +758,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
if (Flags.modesUi()) {
rule.disabledOrigin = ZenModeConfig.ORIGIN_APP;
+ if (Flags.modesCleanupImplicit()) {
+ rule.lastActivation = Instant.ofEpochMilli(123);
+ }
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -770,7 +778,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.condition, fromXml.condition);
assertEquals(rule.enabled, fromXml.enabled);
assertEquals(rule.creationTime, fromXml.creationTime);
- assertEquals(rule.modified, fromXml.modified);
assertEquals(rule.conditionId, fromXml.conditionId);
assertEquals(rule.name, fromXml.name);
assertEquals(rule.zenMode, fromXml.zenMode);
@@ -789,6 +796,9 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.deletionInstant, fromXml.deletionInstant);
if (Flags.modesUi()) {
assertEquals(rule.disabledOrigin, fromXml.disabledOrigin);
+ if (Flags.modesCleanupImplicit()) {
+ assertEquals(rule.lastActivation, fromXml.lastActivation);
+ }
}
}
@@ -916,7 +926,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
assertThat(rule.userModifiedFields).isEqualTo(1);
- assertThat(rule.canBeUpdatedByApp()).isFalse();
+ assertThat(rule.isUserModified()).isTrue();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeRuleXml(rule, baos);
@@ -924,7 +934,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
assertThat(fromXml.userModifiedFields).isEqualTo(rule.userModifiedFields);
- assertThat(fromXml.canBeUpdatedByApp()).isFalse();
+ assertThat(fromXml.isUserModified()).isTrue();
}
@Test
@@ -1259,7 +1269,6 @@ public class ZenModeConfigTest extends UiServiceTestCase {
rule.creationTime = 123;
rule.id = "id";
rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- rule.modified = true;
rule.name = "name";
rule.pkg = "b";
config.automaticRules.put("key", rule);
@@ -1348,7 +1357,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
config.setSuppressedVisualEffects(0);
config.setAllowPriorityChannels(false);
}
- config.areChannelsBypassingDnd = false;
+ config.hasPriorityChannels = false;
return config;
}
@@ -1383,7 +1392,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
config.setSuppressedVisualEffects(0);
config.setAllowPriorityChannels(true);
}
- config.areChannelsBypassingDnd = false;
+ config.hasPriorityChannels = false;
return config;
}
@@ -1410,7 +1419,7 @@ public class ZenModeConfigTest extends UiServiceTestCase {
config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
config.setSuppressedVisualEffects(0);
}
- config.areChannelsBypassingDnd = false;
+ config.hasPriorityChannels = false;
return config;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index b138c72875a6..6d0bf8b322fd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
-import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_UI;
import static com.google.common.truth.Truth.assertThat;
@@ -64,7 +63,6 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
@@ -78,20 +76,14 @@ public class ZenModeDiffTest extends UiServiceTestCase {
// version is not included in the diff; manual & automatic rules have special handling;
// deleted rules are not included in the diff.
public static final Set<String> ZEN_MODE_CONFIG_EXEMPT_FIELDS =
- android.app.Flags.modesApi()
- ? Set.of("version", "manualRule", "automaticRules", "deletedRules")
- : Set.of("version", "manualRule", "automaticRules");
-
- // allowPriorityChannels is flagged by android.app.modes_api
- public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS =
- Set.of("allowPriorityChannels");
+ Set.of("version", "manualRule", "automaticRules", "deletedRules");
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.progressionOf(FLAG_MODES_API, FLAG_MODES_UI);
+ return FlagsParameterization.progressionOf(FLAG_MODES_UI);
}
public ZenModeDiffTest(FlagsParameterization flags) {
@@ -147,7 +139,7 @@ public class ZenModeDiffTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void testRuleDiff_toStringNoChangeAddRemove() throws Exception {
// Start with two identical rules
ZenModeConfig.ZenRule r1 = makeRule();
@@ -164,7 +156,7 @@ public class ZenModeDiffTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void testRuleDiff_toString() throws Exception {
// Start with two identical rules
ZenModeConfig.ZenRule r1 = makeRule();
@@ -218,7 +210,6 @@ public class ZenModeDiffTest extends UiServiceTestCase {
+ "mPriorityCalls:2->1, "
+ "mConversationSenders:2->1, "
+ "mAllowChannels:2->1}, "
- + "modified:true->false, "
+ "pkg:string1->string2, "
+ "zenDeviceEffects:ZenDeviceEffectsDiff{"
+ "mGrayscale:true->false, "
@@ -241,7 +232,7 @@ public class ZenModeDiffTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void testRuleDiff_toStringNullStartPolicy() throws Exception {
// Start with two identical rules
ZenModeConfig.ZenRule r1 = makeRule();
@@ -278,7 +269,6 @@ public class ZenModeDiffTest extends UiServiceTestCase {
+ "creationTime:200->100, "
+ "enabler:string1->string2, "
+ "zenPolicy:ZenPolicyDiff{added}, "
- + "modified:true->false, "
+ "pkg:string1->string2, "
+ "zenDeviceEffects:ZenDeviceEffectsDiff{added}, "
+ "triggerDescription:string1->string2, "
@@ -485,16 +475,10 @@ public class ZenModeDiffTest extends UiServiceTestCase {
// "Metadata" fields are never compared.
Set<String> exemptFields = new LinkedHashSet<>(
Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
- "zenDeviceEffectsUserModifiedFields", "deletionInstant", "disabledOrigin"));
+ "zenDeviceEffectsUserModifiedFields", "deletionInstant", "disabledOrigin",
+ "lastActivation"));
// Flagged fields are only compared if their flag is on.
- if (!Flags.modesApi()) {
- exemptFields.addAll(
- Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
- RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
- RuleDiff.FIELD_ZEN_DEVICE_EFFECTS,
- RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS));
- }
- if (Flags.modesApi() && Flags.modesUi()) {
+ if (Flags.modesUi()) {
exemptFields.add(RuleDiff.FIELD_SNOOZING); // Obsolete.
} else {
exemptFields.add(RuleDiff.FIELD_CONDITION_OVERRIDE);
@@ -530,35 +514,6 @@ public class ZenModeDiffTest extends UiServiceTestCase {
ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
ArrayMap<String, Object> expectedTo = new ArrayMap<>();
List<Field> fieldsForDiff = getFieldsForDiffCheck(
- ZenModeConfig.class, getConfigExemptAndFlaggedFields(), false);
- generateFieldDiffs(c1, c2, fieldsForDiff, expectedFrom, expectedTo);
-
- ZenModeDiff.ConfigDiff d = new ZenModeDiff.ConfigDiff(c1, c2);
- assertTrue(d.hasDiff());
-
- // Now diff them and check that each of the fields has a diff
- for (Field f : fieldsForDiff) {
- String name = f.getName();
- assertNotNull("diff not found for field: " + name, d.getDiffForField(name));
- assertTrue(d.getDiffForField(name).hasDiff());
- assertTrue("unexpected field: " + name, expectedFrom.containsKey(name));
- assertTrue("unexpected field: " + name, expectedTo.containsKey(name));
- assertEquals(expectedFrom.get(name), d.getDiffForField(name).from());
- assertEquals(expectedTo.get(name), d.getDiffForField(name).to());
- }
- }
-
- @Test
- @EnableFlags(FLAG_MODES_API)
- public void testConfigDiff_fieldDiffs_flagOn() throws Exception {
- // these two start the same
- ZenModeConfig c1 = new ZenModeConfig();
- ZenModeConfig c2 = new ZenModeConfig();
-
- // maps mapping field name -> expected output value as we set diffs
- ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
- ArrayMap<String, Object> expectedTo = new ArrayMap<>();
- List<Field> fieldsForDiff = getFieldsForDiffCheck(
ZenModeConfig.class, ZEN_MODE_CONFIG_EXEMPT_FIELDS, false);
generateFieldDiffs(c1, c2, fieldsForDiff, expectedFrom, expectedTo);
@@ -656,14 +611,6 @@ public class ZenModeDiffTest extends UiServiceTestCase {
assertEquals("different", automaticDiffs.get("ruleId").getDiffForField("pkg").to());
}
- // Helper method that merges the base exempt fields with fields that are flagged
- private Set getConfigExemptAndFlaggedFields() {
- Set merged = new HashSet();
- merged.addAll(ZEN_MODE_CONFIG_EXEMPT_FIELDS);
- merged.addAll(ZEN_MODE_CONFIG_FLAGGED_FIELDS);
- return merged;
- }
-
// Helper methods for working with configs, policies, rules
// Just makes a zen rule with fields filled in
private ZenModeConfig.ZenRule makeRule() {
@@ -676,20 +623,17 @@ public class ZenModeDiffTest extends UiServiceTestCase {
rule.creationTime = 123;
rule.id = "ruleId";
rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- rule.modified = false;
rule.name = "name";
rule.setConditionOverride(ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE);
rule.pkg = "a";
- if (android.app.Flags.modesApi()) {
- rule.allowManualInvocation = true;
- rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
- rule.iconResName = "res";
- rule.triggerDescription = "At night";
- rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
- .setShouldDimWallpaper(true)
- .build();
- rule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
- }
+ rule.allowManualInvocation = true;
+ rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
+ rule.iconResName = "res";
+ rule.triggerDescription = "At night";
+ rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build();
+ rule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
return rule;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index a49f5a89b11b..2f0b3ecb593a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -37,7 +37,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.app.Flags;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager.Policy;
@@ -492,8 +491,6 @@ public class ZenModeFilteringTest extends UiServiceTestCase {
@Test
public void testAllowChannels_priorityPackage() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
// Notification with package priority = PRIORITY_MAX (assigned to indicate canBypassDnd)
NotificationRecord r = getNotificationRecord();
r.setPackagePriority(Notification.PRIORITY_MAX);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 31b9cf72584c..0ab11e0cbe3d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -23,7 +23,7 @@ import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.AutomaticZenRule.TYPE_THEATER;
import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
-import static android.app.Flags.FLAG_MODES_API;
+import static android.app.Flags.FLAG_MODES_CLEANUP_IMPLICIT;
import static android.app.Flags.FLAG_MODES_MULTIUSER;
import static android.app.Flags.FLAG_MODES_UI;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
@@ -85,6 +85,7 @@ import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
+import static com.android.os.dnd.DNDProtoEnums.CONV_IMPORTANT;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
@@ -124,7 +125,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
+import static java.time.temporal.ChronoUnit.DAYS;
+
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
@@ -219,11 +223,11 @@ import java.io.Reader;
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneOffset;
-import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -713,7 +717,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testTotalSilence_consolidatedPolicyDisallowsAll() {
// Start with zen mode off just to make sure global/manual mode isn't doing anything.
mZenModeHelper.mZenMode = ZEN_MODE_OFF;
@@ -746,7 +749,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() {
// Start with zen mode off just to make sure global/manual mode isn't doing anything.
mZenModeHelper.mZenMode = ZEN_MODE_OFF;
@@ -1136,8 +1138,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testProto() throws InvalidProtocolBufferException {
mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, null,
- "test", CUSTOM_PKG_UID);
+ ORIGIN_USER_IN_SYSTEMUI, null, "test", CUSTOM_PKG_UID);
mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules
@@ -1262,7 +1263,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testProtoWithAutoRuleCustomPolicy() throws Exception {
setupZenConfig();
// clear any automatic rules just to make sure
@@ -1304,7 +1304,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testProtoWithAutoRuleWithModifiedFields() throws Exception {
setupZenConfig();
mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
@@ -2005,7 +2004,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testReadXml_onModesApi_noUpgrade() throws Exception {
// When reading XML for something that is already on the modes API system, make sure no
// rules' policies get changed.
@@ -2053,7 +2051,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testReadXml_upgradeToModesApi_makesCustomPolicies() throws Exception {
// When reading in an XML file written from a pre-modes-API version, confirm that we create
// a custom policy matching the global config for any automatic rule with no specified
@@ -2105,7 +2102,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testReadXml_upgradeToModesApi_fillsInCustomPolicies() throws Exception {
// When reading in an XML file written from a pre-modes-API version, confirm that for an
// underspecified ZenPolicy, we fill in all of the gaps with things from the global config
@@ -2165,7 +2161,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testReadXml_upgradeToModesApi_existingDefaultRulesGetCustomPolicy()
throws Exception {
setupZenConfig();
@@ -2227,7 +2222,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void testReadXml_upgradeToModesUi_resetsImplicitRuleIcon() throws Exception {
setupZenConfig();
mZenModeHelper.mConfig.automaticRules.clear();
@@ -2241,13 +2236,12 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mConfig.automaticRules.put(implicitRuleBeforeModesUi.id,
implicitRuleBeforeModesUi);
// Plus one other normal rule.
- ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
- anotherRule.id = "other_rule";
+ ZenRule anotherRule = newZenRule("other_rule", "other_pkg", Instant.now());
anotherRule.iconResName = "other_icon";
anotherRule.type = TYPE_IMMERSIVE;
mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
- // Write with pre-modes-ui = (modes_api) version, then re-read.
+ // Write with pre-modes-ui version, then re-read.
ByteArrayOutputStream baos = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_API);
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(new BufferedInputStream(
@@ -2265,7 +2259,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void testReadXml_onModesUi_implicitRulesUntouched() throws Exception {
setupZenConfig();
mZenModeHelper.mConfig.automaticRules.clear();
@@ -2279,8 +2273,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
implicitRuleWithModesUi);
// Plus one other normal rule.
- ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
- anotherRule.id = "other_rule";
+ ZenRule anotherRule = newZenRule("other_rule", "other_pkg", Instant.now());
anotherRule.iconResName = "other_icon";
anotherRule.type = TYPE_IMMERSIVE;
mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
@@ -2342,7 +2335,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// shouldn't update rule that's been modified
ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
- updatedDefaultRule.modified = true;
+ updatedDefaultRule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
updatedDefaultRule.enabled = false;
updatedDefaultRule.creationTime = 0;
updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
@@ -2370,8 +2363,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// will update rule that is not enabled and modified
ZenModeConfig.ZenRule customDefaultRule = new ZenModeConfig.ZenRule();
customDefaultRule.pkg = SystemZenRules.PACKAGE_ANDROID;
+ customDefaultRule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
customDefaultRule.enabled = false;
- customDefaultRule.modified = false;
customDefaultRule.creationTime = 0;
customDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
customDefaultRule.name = "Schedule Default Rule";
@@ -2391,7 +2384,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.ZenRule ruleAfterUpdating =
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID);
assertEquals(customDefaultRule.enabled, ruleAfterUpdating.enabled);
- assertEquals(customDefaultRule.modified, ruleAfterUpdating.modified);
+ assertEquals(customDefaultRule.userModifiedFields, ruleAfterUpdating.userModifiedFields);
assertEquals(customDefaultRule.id, ruleAfterUpdating.id);
assertEquals(customDefaultRule.conditionId, ruleAfterUpdating.conditionId);
assertNotEquals(defaultRuleName, ruleAfterUpdating.name); // update name
@@ -2401,8 +2394,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
- public void testDefaultRulesFromConfig_modesApi_getPolicies() {
+ public void testDefaultRulesFromConfig_getPolicies() {
// After mZenModeHelper was created, set some things in the policy so it's changed from
// default.
setupZenConfig();
@@ -2530,7 +2522,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertTrue(ruleInConfig != null);
assertEquals(zenRule.isEnabled(), ruleInConfig.enabled);
- assertEquals(zenRule.isModified(), ruleInConfig.modified);
assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId);
assertEquals(NotificationManager.zenModeFromInterruptionFilter(
zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode);
@@ -2551,7 +2542,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertTrue(ruleInConfig != null);
assertEquals(zenRule.isEnabled(), ruleInConfig.enabled);
- assertEquals(zenRule.isModified(), ruleInConfig.modified);
assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId);
assertEquals(NotificationManager.zenModeFromInterruptionFilter(
zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode);
@@ -2560,8 +2550,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
- public void testAddAutomaticZenRule_modesApi_fillsInDefaultValues() {
+ public void testAddAutomaticZenRule_fillsInDefaultValues() {
// When a new automatic zen rule is added with only some fields filled in, ensure that
// all unset fields are filled in with device defaults.
@@ -2762,7 +2751,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
ZenDeviceEffects zde =
new ZenDeviceEffects.Builder()
@@ -2799,7 +2787,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() {
ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
@@ -2828,7 +2815,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromUser_respectsHiddenEffects() throws Exception {
ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
@@ -2859,7 +2845,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() {
ZenDeviceEffects original = new ZenDeviceEffects.Builder()
.setShouldDisableTapToWake(true)
@@ -2896,7 +2881,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() {
ZenDeviceEffects original = new ZenDeviceEffects.Builder()
.setShouldDisableTapToWake(true)
@@ -2925,7 +2909,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() {
ZenDeviceEffects original = new ZenDeviceEffects.Builder()
.setShouldDisableTapToWake(true)
@@ -2958,7 +2941,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_nullPolicy_doesNothing() {
// Test that when updateAutomaticZenRule is called with a null policy, nothing changes
// about the existing policy.
@@ -2985,7 +2967,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_overwritesExistingPolicy() {
// Test that when updating an automatic zen rule with an existing policy, the newly set
// fields overwrite those from the previous policy, but unset fields in the new policy
@@ -3024,7 +3005,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -3044,7 +3024,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -3065,7 +3044,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -3086,7 +3064,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -3113,7 +3091,34 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
+ public void getAutomaticZenRules_returnsOwnedRules() {
+ AutomaticZenRule myRule1 = new AutomaticZenRule.Builder("My Rule 1", Uri.parse("1"))
+ .setPackage(mPkg)
+ .setConfigurationActivity(new ComponentName(mPkg, "myActivity"))
+ .build();
+ AutomaticZenRule myRule2 = new AutomaticZenRule.Builder("My Rule 2", Uri.parse("2"))
+ .setPackage(mPkg)
+ .setConfigurationActivity(new ComponentName(mPkg, "myActivity"))
+ .build();
+ AutomaticZenRule otherPkgRule = new AutomaticZenRule.Builder("Other", Uri.parse("3"))
+ .setPackage("com.other.package")
+ .setConfigurationActivity(new ComponentName("com.other.package", "theirActivity"))
+ .build();
+
+ String rule1Id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, myRule1,
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ String rule2Id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, myRule2,
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ String otherRuleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
+ "com.other.package", otherPkgRule, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ Map<String, AutomaticZenRule> rules = mZenModeHelper.getAutomaticZenRules(
+ UserHandle.CURRENT, CUSTOM_PKG_UID);
+
+ assertThat(rules.keySet()).containsExactly(rule1Id, rule2Id);
+ }
+
+ @Test
public void testSetManualZenMode() {
setupZenConfig();
@@ -3133,7 +3138,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
@DisableFlags(FLAG_MODES_UI)
public void setManualZenMode_off_snoozesActiveRules() {
for (ZenChangeOrigin origin : ZenChangeOrigin.values()) {
@@ -3172,7 +3176,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setManualZenMode_off_doesNotSnoozeRulesIfFromUserInSystemUi() {
for (ZenChangeOrigin origin : ZenChangeOrigin.values()) {
// Start with an active rule and an inactive rule
@@ -3246,7 +3250,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Turn zen mode on (to important_interruptions)
// Need to additionally call the looper in order to finish the post-apply-config process
mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "", null, SYSTEM_UID);
+ ORIGIN_USER_IN_SYSTEMUI, "", null, SYSTEM_UID);
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
@@ -3273,14 +3277,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
- assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isEqualTo(
- !(Flags.modesUi() || Flags.modesApi()));
+ assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isFalse();
assertTrue(mZenModeEventLogger.getIsUserAction(0));
assertEquals(SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
// change origin should be populated only under modes_ui
assertThat(mZenModeEventLogger.getChangeOrigin(0)).isEqualTo(
- (Flags.modesApi() && Flags.modesUi()) ? ORIGIN_USER_IN_SYSTEMUI : 0);
+ (Flags.modesUi()) ? ORIGIN_USER_IN_SYSTEMUI : 0);
// and from turning zen mode off:
// - event ID: DND_TURNED_OFF
@@ -3298,11 +3301,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
- if (Flags.modesApi()) {
- assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
- } else {
- checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
- }
+ assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
assertThat(mZenModeEventLogger.getChangeOrigin(1)).isEqualTo(
Flags.modesUi() ? ORIGIN_APP : 0);
}
@@ -3334,8 +3333,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, id, zenRule,
- Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "",
- SYSTEM_UID);
+ ORIGIN_USER_IN_SYSTEMUI, "", SYSTEM_UID);
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
null,
@@ -3345,8 +3343,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String systemId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(), systemRule,
- Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "test",
- SYSTEM_UID);
+ ORIGIN_USER_IN_SYSTEMUI, "test", SYSTEM_UID);
// Event 3: turn on the system rule
mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, systemId,
@@ -3355,8 +3352,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Event 4: "User" deletes the rule
mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, systemId,
- Flags.modesApi() ? ORIGIN_USER_IN_SYSTEMUI : ORIGIN_SYSTEM, "",
- SYSTEM_UID);
+ ORIGIN_USER_IN_SYSTEMUI, "", SYSTEM_UID);
// In total, this represents 4 events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -3394,11 +3390,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertTrue(mZenModeEventLogger.getIsUserAction(1));
assertThat(mZenModeEventLogger.getPackageUid(1)).isEqualTo(
Flags.modesUi() ? CUSTOM_PKG_UID : SYSTEM_UID);
- if (Flags.modesApi()) {
- assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
- } else {
- checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
- }
+ assertThat(mZenModeEventLogger.getPolicyProto(1)).isNull();
assertThat(mZenModeEventLogger.getChangeOrigin(1)).isEqualTo(
Flags.modesUi() ? ORIGIN_USER_IN_SYSTEMUI : 0);
@@ -3426,7 +3418,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_automaticRuleActivatedFromAppByAppAndUser()
throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
@@ -3816,13 +3807,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Now change apps bypassing to true
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
- newConfig.areChannelsBypassingDnd = true;
+ newConfig.hasPriorityChannels = true;
mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, newConfig.toNotificationPolicy(),
ORIGIN_SYSTEM, SYSTEM_UID);
assertEquals(2, mZenModeEventLogger.numLoggedChanges());
// and then back to false, all without changing anything else
- newConfig.areChannelsBypassingDnd = false;
+ newConfig.hasPriorityChannels = false;
mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, newConfig.toNotificationPolicy(),
ORIGIN_SYSTEM, SYSTEM_UID);
assertEquals(3, mZenModeEventLogger.numLoggedChanges());
@@ -3869,10 +3860,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_policyAllowChannels() {
- // when modes_api flag is on, ensure that any change in allow_channels gets logged,
- // even when there are no other changes.
+ // Ensure that any change in allow_channels gets logged, even when there are no other
+ // changes.
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
// Default zen config has allow channels = priority (aka on)
@@ -3919,7 +3909,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3961,7 +3950,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_activeRuleTypes() {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -4050,43 +4038,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_MODES_API)
- public void testUpdateConsolidatedPolicy_preModesApiDefaultRulesOnly_takesGlobalDefault() {
- setupZenConfig();
- // When there's one automatic rule active and it doesn't specify a policy, test that the
- // resulting consolidated policy is one that matches the default rule settings.
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- null,
- new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- null,
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
-
- // enable the rule
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
- new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- ORIGIN_SYSTEM, SYSTEM_UID);
-
- assertEquals(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT),
- mZenModeHelper.getConsolidatedNotificationPolicy());
-
- // inspect the consolidated policy. Based on setupZenConfig() values.
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms());
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia());
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowSystem());
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowReminders());
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowCalls());
- assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConsolidatedPolicy.allowCallsFrom());
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMessages());
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowConversations());
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers());
- assertFalse(mZenModeHelper.mConsolidatedPolicy.showBadges());
- }
-
- @Test
- public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault() {
+ public void testUpdateConsolidatedPolicy_defaultRulesOnly_takesDefault() {
setupZenConfig();
// When there's one automatic rule active and it doesn't specify a policy, test that the
@@ -4113,53 +4065,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_MODES_API)
- public void testUpdateConsolidatedPolicy_preModesApiCustomPolicyOnly_fillInWithGlobal() {
- setupZenConfig();
-
- // when there's only one automatic rule active and it has a custom policy, make sure that's
- // what the consolidated policy reflects whether or not it's stricter than what the global
- // config would specify.
- ZenPolicy customPolicy = new ZenPolicy.Builder()
- .allowAlarms(true) // more lenient than default
- .allowMedia(true) // more lenient than default
- .allowRepeatCallers(false) // more restrictive than default
- .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // more restrictive than default
- .showBadges(true) // more lenient
- .showPeeking(false) // more restrictive
- .build();
-
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- null,
- new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- customPolicy,
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
-
- // enable the rule; this will update the consolidated policy
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
- new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
-
- // since this is the only active rule, the consolidated policy should match the custom
- // policy for every field specified, and take default values (from device default or
- // manual policy) for unspecified things
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); // custom
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMedia()); // custom
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowSystem()); // default
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowReminders()); // default
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowCalls()); // custom
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMessages()); // default
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowConversations()); // default
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()); // custom
- assertTrue(mZenModeHelper.mConsolidatedPolicy.showBadges()); // custom
- assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking()); // custom
- }
-
- @Test
- @EnableFlags(FLAG_MODES_API)
- public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault() {
+ public void testUpdateConsolidatedPolicy_customPolicyOnly_fillInWithDefault() {
setupZenConfig();
// when there's only one automatic rule active and it has a custom policy, make sure that's
@@ -4204,68 +4110,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_MODES_API)
- public void testUpdateConsolidatedPolicy_preModesApiDefaultAndCustomActive_mergesWithGlobal() {
- setupZenConfig();
-
- // when there are two rules active, one inheriting the default policy and one setting its
- // own custom policy, they should be merged to form the most restrictive combination.
-
- // rule 1: no custom policy
- AutomaticZenRule zenRule = new AutomaticZenRule("name",
- null,
- new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- null,
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), zenRule, ORIGIN_SYSTEM, "test", SYSTEM_UID);
-
- // enable rule 1
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id,
- new Condition(zenRule.getConditionId(), "", STATE_TRUE), ORIGIN_SYSTEM, SYSTEM_UID);
-
- // custom policy for rule 2
- ZenPolicy customPolicy = new ZenPolicy.Builder()
- .allowAlarms(true) // more lenient than default
- .allowMedia(true) // more lenient than default
- .allowRepeatCallers(false) // more restrictive than default
- .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // more restrictive than default
- .showBadges(true) // more lenient
- .showPeeking(false) // more restrictive
- .build();
-
- AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
- null,
- new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
- ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- customPolicy,
- NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
- mContext.getPackageName(), zenRule2, ORIGIN_SYSTEM, "test", SYSTEM_UID);
-
- // enable rule 2; this will update the consolidated policy
- mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id2,
- new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
- ORIGIN_SYSTEM, SYSTEM_UID);
-
- // now both rules should be on, and the consolidated policy should reflect the most
- // restrictive option of each of the two
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); // default stricter
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia()); // default stricter
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowSystem()); // default, unset in custom
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowReminders()); // default
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowCalls()); // custom stricter
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMessages()); // default, unset in custom
- assertTrue(mZenModeHelper.mConsolidatedPolicy.allowConversations()); // default
- assertFalse(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()); // custom stricter
- assertFalse(mZenModeHelper.mConsolidatedPolicy.showBadges()); // default stricter
- assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking()); // custom stricter
- }
-
- @Test
- @EnableFlags(FLAG_MODES_API)
- public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault() {
+ public void testUpdateConsolidatedPolicy_defaultAndCustomActive_mergesWithDefault() {
setupZenConfig();
// when there are two rules active, one inheriting the default policy and one setting its
@@ -4328,7 +4173,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testUpdateConsolidatedPolicy_allowChannels() {
setupZenConfig();
@@ -4377,7 +4221,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() {
setupZenConfig();
@@ -4428,7 +4271,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void zenRuleToAutomaticZenRule_allFields() {
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[]{OWNER.getPackageName()});
@@ -4442,7 +4284,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
rule.creationTime = 123;
rule.id = "id";
rule.zenMode = INTERRUPTION_FILTER_ZR;
- rule.modified = true;
rule.name = NAME;
rule.setConditionOverride(OVERRIDE_DEACTIVATE);
rule.pkg = OWNER.getPackageName();
@@ -4473,7 +4314,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void automaticZenRuleToZenRule_allFields() {
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[]{OWNER.getPackageName()});
@@ -4515,7 +4355,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromApp_updatesNameUnlessUserModified() {
// Add a starting rule with the name OriginalName.
AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
@@ -4573,7 +4412,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4627,7 +4465,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromSystemUi_updatesValues() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4678,7 +4515,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromApp_updatesValuesIfRuleNotUserModified() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4754,7 +4590,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_updatesValues() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4777,11 +4612,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
- assertThat(storedRule.canBeUpdatedByApp()).isTrue();
+ assertThat(storedRule.isUserModified()).isFalse();
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_nullDeviceEffectsUpdate() {
// Adds a starting rule with empty zen policies and device effects
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
@@ -4809,7 +4643,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_nullPolicyUpdate() {
// Adds a starting rule with set zen policy and empty device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4838,7 +4671,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void automaticZenRuleToZenRule_nullToNonNullPolicyUpdate() {
when(mContext.checkCallingPermission(anyString()))
.thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -4888,7 +4720,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
STATE_DISALLOW);
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
- assertThat(storedRule.canBeUpdatedByApp()).isFalse();
+ assertThat(storedRule.isUserModified()).isTrue();
assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(
ZenPolicy.FIELD_ALLOW_CHANNELS
| ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS
@@ -4902,7 +4734,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void automaticZenRuleToZenRule_nullToNonNullDeviceEffectsUpdate() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4931,7 +4762,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
- assertThat(storedRule.canBeUpdatedByApp()).isFalse();
+ assertThat(storedRule.isUserModified()).isTrue();
assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(
ZenDeviceEffects.FIELD_GRAYSCALE);
}
@@ -5007,7 +4838,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception {
setupZenConfig();
@@ -5047,7 +4877,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception {
setupZenConfig();
@@ -5091,7 +4920,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception {
setupZenConfig();
@@ -5167,7 +4995,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_ruleChanged_deactivatesRule() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5191,7 +5018,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_ruleNotChanged_doesNotDeactivateRule() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5214,7 +5040,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_ruleChangedByUser_doesNotDeactivateRule() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5239,7 +5065,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_ruleChangedByUser_doesNotDeactivateRule_forWatch() {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true);
@@ -5266,7 +5091,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_ruleDisabledByUser_doesNotReactivateOnReenable() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5291,7 +5116,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_changeOwnerForSystemRule_allowed() {
when(mContext.checkCallingPermission(anyString()))
.thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -5314,7 +5139,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_changeOwnerForAppOwnedRule_ignored() {
AutomaticZenRule original = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setOwner(new ComponentName(mContext.getPackageName(), "old.third.party.cps"))
@@ -5335,7 +5160,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
reset(mDeviceEffectsApplier);
@@ -5358,7 +5182,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_applied() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(ORIGIN_INIT));
@@ -5384,7 +5207,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testSettingDeviceEffects_throwsExceptionIfAlreadySet() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
@@ -5394,7 +5216,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_onDeactivateRule_applied() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
@@ -5413,7 +5234,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_changeToConsolidatedEffects_applied() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(ORIGIN_INIT));
@@ -5453,7 +5273,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(ORIGIN_INIT));
@@ -5478,7 +5297,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_activeBeforeApplierProvided_appliedWhenProvided() {
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
String ruleId = addRuleWithEffects(zde);
@@ -5494,7 +5312,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_onUserSwitch_appliedImmediately() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(ORIGIN_INIT));
@@ -5522,7 +5339,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI, FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING})
+ @EnableFlags({FLAG_MODES_UI, FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING})
public void testDeviceEffects_allowsGrayscale() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
reset(mDeviceEffectsApplier);
@@ -5539,7 +5356,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI, FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING})
+ @EnableFlags({FLAG_MODES_UI, FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING})
public void testDeviceEffects_whileDriving_avoidsGrayscale() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
reset(mDeviceEffectsApplier);
@@ -5563,7 +5380,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI, FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING})
+ @EnableFlags({FLAG_MODES_UI, FLAG_PREVENT_ZEN_DEVICE_EFFECTS_WHILE_DRIVING})
public void testDeviceEffects_whileDrivingWithGrayscale_allowsGrayscale() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
reset(mDeviceEffectsApplier);
@@ -5594,7 +5411,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() {
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5651,7 +5467,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() {
// Start with a single rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5685,7 +5500,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() {
// Start with a single rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5736,7 +5550,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() {
// Start with a single rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5779,7 +5592,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void removeAndAddAutomaticZenRule_ifChangingComponent_isAllowedAndDoesNotRestore() {
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5824,7 +5637,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() {
mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match.
@@ -5874,7 +5686,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAllZenRules_preservedForRestoring() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5898,14 +5709,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() {
mZenModeHelper.mConfig.automaticRules.clear();
// Start with deleted rules from 2 different packages.
Instant now = Instant.ofEpochMilli(1701796461000L);
- ZenRule pkg1Rule = newZenRule("pkg1", now.minus(1, ChronoUnit.DAYS), now);
- ZenRule pkg2Rule = newZenRule("pkg2", now.minus(2, ChronoUnit.DAYS), now);
+ ZenRule pkg1Rule = newDeletedZenRule("1", "pkg1", now.minus(1, DAYS), now);
+ ZenRule pkg2Rule = newDeletedZenRule("2", "pkg2", now.minus(2, DAYS), now);
mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg1Rule), pkg1Rule);
mZenModeHelper.mConfig.deletedRules.put(ZenModeConfig.deletedRuleKey(pkg2Rule), pkg2Rule);
@@ -5918,7 +5728,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasActive_isRestoredAsInactive() {
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5968,7 +5777,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasSnoozed_isRestoredAsInactive() {
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6023,12 +5831,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testRuleCleanup() throws Exception {
Instant now = Instant.ofEpochMilli(1701796461000L);
- Instant yesterday = now.minus(1, ChronoUnit.DAYS);
- Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS);
- Instant twoMonthsAgo = now.minus(60, ChronoUnit.DAYS);
+ Instant yesterday = now.minus(1, DAYS);
+ Instant aWeekAgo = now.minus(7, DAYS);
+ Instant twoMonthsAgo = now.minus(60, DAYS);
mTestClock.setNowMillis(now.toEpochMilli());
when(mPackageManager.getPackageInfo(eq("good_pkg"), anyInt()))
@@ -6041,24 +5848,28 @@ public class ZenModeHelperTest extends UiServiceTestCase {
config.user = 42;
mZenModeHelper.mConfigs.put(42, config);
// okay rules (not deleted, package exists, with a range of creation dates).
- config.automaticRules.put("ar1", newZenRule("good_pkg", now, null));
- config.automaticRules.put("ar2", newZenRule("good_pkg", yesterday, null));
- config.automaticRules.put("ar3", newZenRule("good_pkg", twoMonthsAgo, null));
+ config.automaticRules.put("ar1", newZenRule("ar1", "good_pkg", now));
+ config.automaticRules.put("ar2", newZenRule("ar2", "good_pkg", yesterday));
+ config.automaticRules.put("ar3", newZenRule("ar3", "good_pkg", twoMonthsAgo));
// newish rules for a missing package
- config.automaticRules.put("ar4", newZenRule("bad_pkg", yesterday, null));
+ config.automaticRules.put("ar4", newZenRule("ar4", "bad_pkg", yesterday));
// oldish rules belonging to a missing package
- config.automaticRules.put("ar5", newZenRule("bad_pkg", aWeekAgo, null));
+ config.automaticRules.put("ar5", newZenRule("ar5", "bad_pkg", aWeekAgo));
// rules deleted recently
- config.deletedRules.put("del1", newZenRule("good_pkg", twoMonthsAgo, yesterday));
- config.deletedRules.put("del2", newZenRule("good_pkg", twoMonthsAgo, aWeekAgo));
+ config.deletedRules.put("del1",
+ newDeletedZenRule("del1", "good_pkg", twoMonthsAgo, yesterday));
+ config.deletedRules.put("del2",
+ newDeletedZenRule("del2", "good_pkg", twoMonthsAgo, aWeekAgo));
// rules deleted a long time ago
- config.deletedRules.put("del3", newZenRule("good_pkg", twoMonthsAgo, twoMonthsAgo));
+ config.deletedRules.put("del3",
+ newDeletedZenRule("del3", "good_pkg", twoMonthsAgo, twoMonthsAgo));
// rules for a missing package, created recently and deleted recently
- config.deletedRules.put("del4", newZenRule("bad_pkg", yesterday, now));
+ config.deletedRules.put("del4", newDeletedZenRule("del4", "bad_pkg", yesterday, now));
// rules for a missing package, created a long time ago and deleted recently
- config.deletedRules.put("del5", newZenRule("bad_pkg", twoMonthsAgo, now));
+ config.deletedRules.put("del5", newDeletedZenRule("del5", "bad_pkg", twoMonthsAgo, now));
// rules for a missing package, created a long time ago and deleted a long time ago
- config.deletedRules.put("del6", newZenRule("bad_pkg", twoMonthsAgo, twoMonthsAgo));
+ config.deletedRules.put("del6",
+ newDeletedZenRule("del6", "bad_pkg", twoMonthsAgo, twoMonthsAgo));
mZenModeHelper.onUserSwitched(42); // copies config and cleans it up.
@@ -6068,20 +5879,120 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.containsExactly("del1", "del2", "del4");
}
- private static ZenRule newZenRule(String pkg, Instant createdAt, @Nullable Instant deletedAt) {
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void testRuleCleanup_removesNotRecentlyUsedNotModifiedImplicitRules() throws Exception {
+ Instant now = Instant.ofEpochMilli(1701796461000L);
+ Instant yesterday = now.minus(1, DAYS);
+ Instant aWeekAgo = now.minus(7, DAYS);
+ Instant twoMonthsAgo = now.minus(60, DAYS);
+ Instant aYearAgo = now.minus(365, DAYS);
+ mTestClock.setNowMillis(now.toEpochMilli());
+ when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(new PackageInfo());
+
+ // Set up a config to be loaded, containing a bunch of implicit rules
+ ZenModeConfig config = new ZenModeConfig();
+ config.user = 42;
+ mZenModeHelper.mConfigs.put(42, config);
+ // used recently
+ ZenRule usedRecently1 = newImplicitZenRule("pkg1", aYearAgo, yesterday);
+ ZenRule usedRecently2 = newImplicitZenRule("pkg2", aYearAgo, aWeekAgo);
+ config.automaticRules.put(usedRecently1.id, usedRecently1);
+ config.automaticRules.put(usedRecently2.id, usedRecently2);
+ // not used in a long time
+ ZenRule longUnused = newImplicitZenRule("pkg3", aYearAgo, twoMonthsAgo);
+ config.automaticRules.put(longUnused.id, longUnused);
+ // created a long time ago, before lastActivation tracking
+ ZenRule oldAndLastUsageUnknown = newImplicitZenRule("pkg4", twoMonthsAgo, null);
+ config.automaticRules.put(oldAndLastUsageUnknown.id, oldAndLastUsageUnknown);
+ // created a short time ago, before lastActivation tracking
+ ZenRule newAndLastUsageUnknown = newImplicitZenRule("pkg5", aWeekAgo, null);
+ config.automaticRules.put(newAndLastUsageUnknown.id, newAndLastUsageUnknown);
+ // not used in a long time, but was customized by user
+ ZenRule longUnusedButCustomized = newImplicitZenRule("pkg6", aYearAgo, twoMonthsAgo);
+ longUnusedButCustomized.zenPolicyUserModifiedFields = ZenPolicy.FIELD_CONVERSATIONS;
+ config.automaticRules.put(longUnusedButCustomized.id, longUnusedButCustomized);
+ // created a long time ago, before lastActivation tracking, and was customized by user
+ ZenRule oldAndLastUsageUnknownAndCustomized = newImplicitZenRule("pkg7", twoMonthsAgo,
+ null);
+ oldAndLastUsageUnknownAndCustomized.userModifiedFields = AutomaticZenRule.FIELD_ICON;
+ config.automaticRules.put(oldAndLastUsageUnknownAndCustomized.id,
+ oldAndLastUsageUnknownAndCustomized);
+
+ mZenModeHelper.onUserSwitched(42); // copies config and cleans it up.
+
+ // The recently used OR modified OR last-used-unknown rules stay.
+ assertThat(mZenModeHelper.mConfig.automaticRules.values())
+ .comparingElementsUsing(IGNORE_METADATA)
+ .containsExactly(usedRecently1, usedRecently2, oldAndLastUsageUnknown,
+ newAndLastUsageUnknown, longUnusedButCustomized,
+ oldAndLastUsageUnknownAndCustomized);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void testRuleCleanup_assignsLastActivationToImplicitRules() throws Exception {
+ Instant now = Instant.ofEpochMilli(1701796461000L);
+ Instant aWeekAgo = now.minus(7, DAYS);
+ Instant aYearAgo = now.minus(365, DAYS);
+ mTestClock.setNowMillis(now.toEpochMilli());
+ when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(new PackageInfo());
+
+ // Set up a config to be loaded, containing implicit rules.
+ ZenModeConfig config = new ZenModeConfig();
+ config.user = 42;
+ mZenModeHelper.mConfigs.put(42, config);
+ // with last activation known
+ ZenRule usedRecently = newImplicitZenRule("pkg1", aYearAgo, aWeekAgo);
+ config.automaticRules.put(usedRecently.id, usedRecently);
+ // created a long time ago, with last activation unknown
+ ZenRule oldAndLastUsageUnknown = newImplicitZenRule("pkg4", aYearAgo, null);
+ config.automaticRules.put(oldAndLastUsageUnknown.id, oldAndLastUsageUnknown);
+ // created a short time ago, with last activation unknown
+ ZenRule newAndLastUsageUnknown = newImplicitZenRule("pkg5", aWeekAgo, null);
+ config.automaticRules.put(newAndLastUsageUnknown.id, newAndLastUsageUnknown);
+
+ mZenModeHelper.onUserSwitched(42); // copies config and cleans it up.
+
+ // All rules stayed.
+ usedRecently = getZenRule(usedRecently.id);
+ oldAndLastUsageUnknown = getZenRule(oldAndLastUsageUnknown.id);
+ newAndLastUsageUnknown = getZenRule(newAndLastUsageUnknown.id);
+
+ // The rules with an unknown last usage have been assigned a placeholder one.
+ assertThat(usedRecently.lastActivation).isEqualTo(aWeekAgo);
+ assertThat(oldAndLastUsageUnknown.lastActivation).isEqualTo(now);
+ assertThat(newAndLastUsageUnknown.lastActivation).isEqualTo(now);
+ }
+
+ private static ZenRule newDeletedZenRule(String id, String pkg, Instant createdAt,
+ @NonNull Instant deletedAt) {
+ ZenRule rule = newZenRule(id, pkg, createdAt);
+ rule.deletionInstant = deletedAt;
+ return rule;
+ }
+
+ private static ZenRule newImplicitZenRule(String pkg, @NonNull Instant createdAt,
+ @Nullable Instant lastActivatedAt) {
+ ZenRule implicitRule = newZenRule(implicitRuleId(pkg), pkg, createdAt);
+ implicitRule.lastActivation = lastActivatedAt;
+ return implicitRule;
+ }
+
+ private static ZenRule newZenRule(String id, String pkg, Instant createdAt) {
ZenRule rule = new ZenRule();
+ rule.id = id;
rule.pkg = pkg;
rule.creationTime = createdAt.toEpochMilli();
rule.enabled = true;
- rule.deletionInstant = deletedAt;
+ rule.deletionInstant = null;
// Plus stuff so that isValidAutomaticRule() passes
- rule.name = "A rule from " + pkg + " created on " + createdAt;
+ rule.name = "Rule " + id;
rule.conditionId = Uri.parse(rule.name);
return rule;
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
mContext.getPackageName(),
@@ -6112,14 +6023,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
// Assume existence of a system-owned rule that is currently ACTIVE.
- ZenRule systemRule = newZenRule("android", Instant.now(), null);
+ ZenRule systemRule = newZenRule("systemRule", "android", Instant.now());
systemRule.zenMode = ZEN_MODE_ALARMS;
systemRule.condition = new Condition(systemRule.conditionId, "on", Condition.STATE_TRUE);
ZenModeConfig config = mZenModeHelper.mConfig.copy();
- config.automaticRules.put("systemRule", systemRule);
+ config.automaticRules.put(systemRule.id, systemRule);
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
@@ -6128,15 +6038,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
- ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ ZenRule otherRule = newZenRule("otherRule", "another.package", Instant.now());
otherRule.zenMode = ZEN_MODE_ALARMS;
otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
ZenModeConfig config = mZenModeHelper.mConfig.copy();
- config.automaticRules.put("otherRule", otherRule);
+ config.automaticRules.put(otherRule.id, otherRule);
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
@@ -6149,15 +6058,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void setAutomaticZenRuleStateFromConditionProvider_conditionForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
- ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ ZenRule otherRule = newZenRule("otherRule", "another.package", Instant.now());
otherRule.zenMode = ZEN_MODE_ALARMS;
otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
ZenModeConfig config = mZenModeHelper.mConfig.copy();
- config.automaticRules.put("otherRule", otherRule);
+ config.automaticRules.put(otherRule.id, otherRule);
mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
@@ -6171,7 +6079,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testCallbacks_policy() throws Exception {
setupZenConfig();
assertThat(mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT).allowReminders())
@@ -6193,7 +6100,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void testCallbacks_consolidatedPolicy() throws Exception {
assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowMedia()).isTrue();
SettableFuture<Policy> futureConsolidatedPolicy = SettableFuture.create();
@@ -6219,7 +6125,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6235,7 +6140,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6257,7 +6161,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -6290,7 +6193,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_ruleCustomizedButNotFilter_updatesRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -6322,7 +6224,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, mPkg, CUSTOM_PKG_UID,
@@ -6339,7 +6240,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6350,7 +6250,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6375,7 +6274,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void applyGlobalZenModeAsImplicitZenRule_again_refreshesRuleName() {
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
@@ -6394,7 +6293,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void applyGlobalZenModeAsImplicitZenRule_again_doesNotChangeCustomizedRuleName() {
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
@@ -6420,19 +6319,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_MODES_API)
- public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
- mZenModeHelper.mConfig.automaticRules.clear();
-
- withoutWtfCrash(
- () -> mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
- CUSTOM_PKG_NAME, CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS));
-
- assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
- }
-
- @Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6457,7 +6343,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() {
mZenModeHelper.mConfig.automaticRules.clear();
@@ -6489,7 +6374,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -6532,7 +6416,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_ruleCustomizedButNotZenPolicy_updatesRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -6572,7 +6455,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void applyGlobalPolicyAsImplicitZenRule_again_refreshesRuleName() {
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
@@ -6591,7 +6474,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void applyGlobalPolicyAsImplicitZenRule_again_doesNotChangeCustomizedRuleName() {
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
@@ -6617,19 +6500,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_MODES_API)
- public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
- mZenModeHelper.mConfig.automaticRules.clear();
-
- withoutWtfCrash(
- () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
- CUSTOM_PKG_NAME, CUSTOM_PKG_UID, new Policy(0, 0, 0)));
-
- assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
- }
-
- @Test
- @EnableFlags(FLAG_MODES_API)
public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() {
Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
@@ -6645,7 +6515,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
@DisableFlags(FLAG_MODES_UI)
public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_copiesGlobalPolicy() {
// Implicit rule will get the global policy at the time of rule creation.
@@ -6665,7 +6534,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
Policy policy = new Policy(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_STARRED, 0);
mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, policy, ORIGIN_APP,
@@ -6680,7 +6548,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
@DisableFlags(FLAG_MODES_UI)
public void setNotificationPolicy_updatesRulePolicies_ifRulePolicyIsDefaultOrGlobalPolicy() {
ZenPolicy defaultZenPolicy = mZenModeHelper.getDefaultZenPolicy();
@@ -6726,7 +6593,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
public void addRule_iconIdWithResourceNameTooLong_ignoresIcon() {
int resourceId = 999;
String veryLongResourceName = "com.android.server.notification:drawable/"
@@ -6745,7 +6611,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setManualZenRuleDeviceEffects_noPreexistingMode() {
ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
@@ -6759,7 +6625,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setManualZenRuleDeviceEffects_preexistingMode() {
mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_OFF, Uri.EMPTY,
ORIGIN_USER_IN_SYSTEMUI, "create manual rule", "settings", SYSTEM_UID);
@@ -6776,7 +6642,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void addAutomaticZenRule_startsDisabled_recordsDisabledOrigin() {
AutomaticZenRule startsDisabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setOwner(new ComponentName(mPkg, "SomeProvider"))
@@ -6792,7 +6658,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_disabling_recordsDisabledOrigin() {
AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setOwner(new ComponentName(mPkg, "SomeProvider"))
@@ -6815,7 +6681,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_keepingDisabled_preservesPreviousDisabledOrigin() {
AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setOwner(new ComponentName(mPkg, "SomeProvider"))
@@ -6845,7 +6711,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateAutomaticZenRule_enabling_clearsDisabledOrigin() {
AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
.setOwner(new ComponentName(mPkg, "SomeProvider"))
@@ -6875,7 +6741,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualActivation_appliesOverride() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -6893,7 +6759,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualActivationAndThenDeactivation_removesOverride() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -6930,7 +6796,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualDeactivationAndThenReactivation_removesOverride() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -6976,7 +6842,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualDeactivation_appliesOverride() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -7002,7 +6868,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_ifManualActive_appCannotDeactivateBeforeActivating() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -7039,7 +6905,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_ifManualInactive_appCannotReactivateBeforeDeactivating() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -7085,7 +6951,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_withActivationOverride_userActionFromAppCanDeactivate() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -7109,7 +6975,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_withDeactivationOverride_userActionFromAppCanActivate() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -7140,7 +7006,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_manualActionFromApp_isNotOverride() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
.setPackage(mPkg)
@@ -7165,7 +7031,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_implicitRuleManualActivation_doesNotUseOverride() {
mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
PERMISSION_GRANTED); // So that canManageAZR succeeds.
@@ -7188,7 +7054,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_implicitRuleManualDeactivation_doesNotUseOverride() {
mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
PERMISSION_GRANTED); // So that canManageAZR succeeds.
@@ -7214,24 +7080,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags({FLAG_MODES_API, FLAG_MODES_UI})
- public void testDefaultConfig_preModesApi_rulesAreBare() {
- // Create a new user, which should get a copy of the default policy.
- mZenModeHelper.onUserSwitched(101);
-
- ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
- ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
-
- assertThat(eventsRule).isNotNull();
- assertThat(eventsRule.zenPolicy).isNull();
- assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
- assertThat(eventsRule.triggerDescription).isNull();
- }
-
- @Test
- @EnableFlags(FLAG_MODES_API)
@DisableFlags(FLAG_MODES_UI)
- public void testDefaultConfig_modesApi_rulesHaveFullPolicy() {
+ public void testDefaultConfig_rulesHaveFullPolicy() {
// Create a new user, which should get a copy of the default policy.
mZenModeHelper.onUserSwitched(201);
@@ -7245,7 +7095,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void testDefaultConfig_modesUi_rulesHaveFullPolicy() {
// Create a new user, which should get a copy of the default policy.
mZenModeHelper.onUserSwitched(301);
@@ -7260,7 +7110,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_withManualActivation_activeOnReboot()
throws Exception {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
@@ -7298,7 +7148,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void setAutomaticZenRuleState_withManualDeactivation_clearedOnReboot()
throws Exception {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
@@ -7336,7 +7186,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withoutPolicy_getsItsOwnInstanceOfDefaultPolicy() {
// Add a rule without policy -> uses default config
AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
@@ -7352,7 +7201,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void readXml_withDisabledEventsRule_deletesIt() throws Exception {
ZenRule rule = new ZenRule();
rule.id = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
@@ -7372,7 +7221,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void readXml_withEnabledEventsRule_keepsIt() throws Exception {
ZenRule rule = new ZenRule();
rule.id = ZenModeConfig.EVENTS_OBSOLETE_RULE_ID;
@@ -7392,7 +7241,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
- @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ @EnableFlags(FLAG_MODES_UI)
public void updateHasPriorityChannels_keepsChannelSettings() {
setupZenConfig();
@@ -7512,6 +7361,125 @@ public class ZenModeHelperTest extends UiServiceTestCase {
"config: setAzrStateFromCps: cond/cond (ORIGIN_APP) from uid " + CUSTOM_PKG_UID);
}
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void setAutomaticZenRuleState_updatesLastActivation() {
+ String ruleOne = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+ new AutomaticZenRule.Builder("rule", CONDITION_ID)
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(),
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+ String ruleTwo = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+ new AutomaticZenRule.Builder("unrelated", Uri.parse("other.condition"))
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(),
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isNull();
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+
+ Instant firstActivation = Instant.ofEpochMilli(100);
+ mTestClock.setNow(firstActivation);
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleOne, CONDITION_TRUE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isEqualTo(firstActivation);
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+
+ mTestClock.setNow(Instant.ofEpochMilli(300));
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleOne, CONDITION_FALSE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isEqualTo(firstActivation);
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+
+ Instant secondActivation = Instant.ofEpochMilli(500);
+ mTestClock.setNow(secondActivation);
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleOne, CONDITION_TRUE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleOne).lastActivation).isEqualTo(secondActivation);
+ assertThat(getZenRule(ruleTwo).lastActivation).isNull();
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void setManualZenMode_updatesLastActivation() {
+ assertThat(mZenModeHelper.mConfig.manualRule.lastActivation).isNull();
+ Instant instant = Instant.ofEpochMilli(100);
+ mTestClock.setNow(instant);
+
+ mZenModeHelper.setManualZenMode(UserHandle.CURRENT, ZEN_MODE_ALARMS, null,
+ ORIGIN_USER_IN_SYSTEMUI, "reason", "systemui", SYSTEM_UID);
+
+ assertThat(mZenModeHelper.mConfig.manualRule.lastActivation).isEqualTo(instant);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void applyGlobalZenModeAsImplicitZenRule_updatesLastActivation() {
+ Instant instant = Instant.ofEpochMilli(100);
+ mTestClock.setNow(instant);
+
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT, CUSTOM_PKG_NAME,
+ CUSTOM_PKG_UID, ZEN_MODE_ALARMS);
+
+ ZenRule implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
+ assertThat(implicitRule.lastActivation).isEqualTo(instant);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void setAutomaticZenRuleState_notChangingActiveState_doesNotUpdateLastActivation() {
+ String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg,
+ new AutomaticZenRule.Builder("rule", CONDITION_ID)
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build(),
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isNull();
+
+ // Manual activation comes first
+ Instant firstActivation = Instant.ofEpochMilli(100);
+ mTestClock.setNow(firstActivation);
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isEqualTo(firstActivation);
+
+ // Now the app says the rule should be active (assume it's on a schedule, and the app
+ // doesn't listen to broadcasts so it doesn't know an override was present). This doesn't
+ // change the activation state.
+ mTestClock.setNow(Instant.ofEpochMilli(300));
+ mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CONDITION_TRUE,
+ ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isEqualTo(firstActivation);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_UI, FLAG_MODES_CLEANUP_IMPLICIT})
+ public void addOrUpdateRule_doesNotUpdateLastActivation() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("rule", CONDITION_ID)
+ .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build();
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mPkg, azr,
+ ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isNull();
+
+ mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
+ new AutomaticZenRule.Builder(azr).setName("New name").build(), ORIGIN_APP, "reason",
+ CUSTOM_PKG_UID);
+
+ assertThat(getZenRule(ruleId).lastActivation).isNull();
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
@@ -7529,22 +7497,27 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
private static final Correspondence<ZenRule, ZenRule> IGNORE_METADATA =
- Correspondence.transforming(zr -> {
- Parcel p = Parcel.obtain();
- try {
- zr.writeToParcel(p, 0);
- p.setDataPosition(0);
- ZenRule copy = new ZenRule(p);
- copy.creationTime = 0;
- copy.userModifiedFields = 0;
- copy.zenPolicyUserModifiedFields = 0;
- copy.zenDeviceEffectsUserModifiedFields = 0;
- return copy;
- } finally {
- p.recycle();
- }
- },
- "Ignoring timestamp and userModifiedFields");
+ Correspondence.transforming(
+ ZenModeHelperTest::cloneWithoutMetadata,
+ ZenModeHelperTest::cloneWithoutMetadata,
+ "Ignoring timestamps and userModifiedFields");
+
+ private static ZenRule cloneWithoutMetadata(ZenRule rule) {
+ Parcel p = Parcel.obtain();
+ try {
+ rule.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ ZenRule copy = new ZenRule(p);
+ copy.creationTime = 0;
+ copy.userModifiedFields = 0;
+ copy.zenPolicyUserModifiedFields = 0;
+ copy.zenDeviceEffectsUserModifiedFields = 0;
+ copy.lastActivation = null;
+ return copy;
+ } finally {
+ p.recycle();
+ }
+ }
private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
@Nullable Boolean conditionActive) {
@@ -7574,7 +7547,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
return rule;
}
- // TODO: b/310620812 - Update setup methods to include allowChannels() when MODES_API is inlined
private void setupZenConfig() {
Policy customPolicy = new Policy(PRIORITY_CATEGORY_REMINDERS
| PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES
@@ -7583,7 +7555,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
PRIORITY_SENDERS_STARRED,
PRIORITY_SENDERS_STARRED,
SUPPRESSED_EFFECT_BADGE,
- 0,
+ 0, // allows priority channels.
CONVERSATION_SENDERS_IMPORTANT);
mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, customPolicy, ORIGIN_UNKNOWN, 1);
if (!Flags.modesUi()) {
@@ -7599,8 +7571,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(STATE_ALLOW, dndProto.getCalls().getNumber());
assertEquals(PEOPLE_STARRED, dndProto.getAllowCallsFrom().getNumber());
assertEquals(STATE_ALLOW, dndProto.getMessages().getNumber());
+ assertEquals(PEOPLE_STARRED, dndProto.getAllowMessagesFrom().getNumber());
assertEquals(STATE_ALLOW, dndProto.getEvents().getNumber());
assertEquals(STATE_ALLOW, dndProto.getRepeatCallers().getNumber());
+ assertEquals(STATE_ALLOW, dndProto.getConversations().getNumber());
+ assertEquals(CONV_IMPORTANT, dndProto.getAllowConversationsFrom().getNumber());
assertEquals(STATE_ALLOW, dndProto.getFullscreen().getNumber());
assertEquals(STATE_ALLOW, dndProto.getLights().getNumber());
assertEquals(STATE_ALLOW, dndProto.getPeek().getNumber());
@@ -7616,7 +7591,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
return;
}
- // When modes_api flag is on, the default zen config is the device defaults.
+ // The default zen config is the device defaults.
assertThat(dndProto.getAlarms().getNumber()).isEqualTo(STATE_ALLOW);
assertThat(dndProto.getMedia().getNumber()).isEqualTo(STATE_ALLOW);
assertThat(dndProto.getSystem().getNumber()).isEqualTo(STATE_DISALLOW);
@@ -7627,6 +7602,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(dndProto.getAllowMessagesFrom().getNumber()).isEqualTo(PEOPLE_STARRED);
assertThat(dndProto.getEvents().getNumber()).isEqualTo(STATE_DISALLOW);
assertThat(dndProto.getRepeatCallers().getNumber()).isEqualTo(STATE_ALLOW);
+ assertThat(dndProto.getConversations().getNumber()).isEqualTo(STATE_ALLOW);
+ assertThat(dndProto.getAllowConversationsFrom().getNumber()).isEqualTo(CONV_IMPORTANT);
assertThat(dndProto.getFullscreen().getNumber()).isEqualTo(STATE_DISALLOW);
assertThat(dndProto.getLights().getNumber()).isEqualTo(STATE_DISALLOW);
assertThat(dndProto.getPeek().getNumber()).isEqualTo(STATE_DISALLOW);
@@ -7634,6 +7611,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(dndProto.getBadge().getNumber()).isEqualTo(STATE_ALLOW);
assertThat(dndProto.getAmbient().getNumber()).isEqualTo(STATE_DISALLOW);
assertThat(dndProto.getNotificationList().getNumber()).isEqualTo(STATE_ALLOW);
+ assertThat(dndProto.getAllowChannels().getNumber()).isEqualTo(
+ DNDProtoEnums.CHANNEL_POLICY_PRIORITY);
}
private static String getZenLog() {
@@ -7642,16 +7621,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
return zenLogWriter.toString();
}
- private static void withoutWtfCrash(Runnable test) {
- Log.TerribleFailureHandler oldHandler = Log.setWtfHandler((tag, what, system) -> {
- });
- try {
- test.run();
- } finally {
- Log.setWtfHandler(oldHandler);
- }
- }
-
/**
* Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
*/
@@ -7954,6 +7923,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
return mNowMillis;
}
+ private void setNow(Instant instant) {
+ mNowMillis = instant.toEpochMilli();
+ }
+
private void setNowMillis(long millis) {
mNowMillis = millis;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
index 6433b76defc3..8b9376454c0f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.fail;
-import android.app.Flags;
import android.os.Parcel;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
@@ -34,7 +33,6 @@ import com.android.server.UiServiceTestCase;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,11 +48,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- @Before
- public final void setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
- }
-
@Test
public void testZenPolicyApplyAllowedToDisallowed() {
ZenPolicy.Builder builder = new ZenPolicy.Builder();
@@ -207,8 +200,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testZenPolicyApplyChannels_applyUnset() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy.Builder builder = new ZenPolicy.Builder();
ZenPolicy unset = builder.build();
@@ -223,8 +214,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testZenPolicyApplyChannels_applyStricter() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy.Builder builder = new ZenPolicy.Builder();
builder.allowPriorityChannels(false);
ZenPolicy none = builder.build();
@@ -239,8 +228,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testZenPolicyApplyChannels_applyLooser() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy.Builder builder = new ZenPolicy.Builder();
builder.allowPriorityChannels(false);
ZenPolicy none = builder.build();
@@ -255,8 +242,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testZenPolicyApplyChannels_applySet() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy.Builder builder = new ZenPolicy.Builder();
ZenPolicy unset = builder.build();
@@ -270,8 +255,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testZenPolicyOverwrite_allUnsetPolicies() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy.Builder builder = new ZenPolicy.Builder();
ZenPolicy unset = builder.build();
@@ -292,8 +275,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testZenPolicyOverwrite_someOverlappingFields_takeNewPolicy() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy p1 = new ZenPolicy.Builder()
.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
.allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED)
@@ -375,7 +356,6 @@ public class ZenPolicyTest extends UiServiceTestCase {
@Test
public void testEmptyZenPolicy_emptyChannels() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenPolicy.Builder builder = new ZenPolicy.Builder();
ZenPolicy policy = builder.build();
@@ -688,22 +668,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
}
@Test
- public void testAllowChannels_noFlag() {
- mSetFlagsRule.disableFlags(Flags.FLAG_MODES_API);
-
- // allowChannels should be unset, not be modifiable, and not show up in any output
- ZenPolicy.Builder builder = new ZenPolicy.Builder();
- builder.allowPriorityChannels(true);
- ZenPolicy policy = builder.build();
-
- assertThat(policy.getPriorityChannelsAllowed()).isEqualTo(ZenPolicy.STATE_UNSET);
- assertThat(policy.toString().contains("allowChannels")).isFalse();
- }
-
- @Test
public void testAllowChannels() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
// allow priority channels
ZenPolicy.Builder builder = new ZenPolicy.Builder();
builder.allowPriorityChannels(true);
diff --git a/services/tests/wmtests/res/values/styles.xml b/services/tests/wmtests/res/values/styles.xml
index 6857ff99e9b8..6ded2b557bcc 100644
--- a/services/tests/wmtests/res/values/styles.xml
+++ b/services/tests/wmtests/res/values/styles.xml
@@ -21,4 +21,14 @@
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
+
+ <style name="ActivityWindowStyleTest">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowShowWallpaper">true</item>
+ <item name="android:windowNoDisplay">true</item>
+ <item name="android:windowDisablePreview">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ <item name="android:windowSplashScreenBehavior">icon_preferred</item>
+ </style>
</resources>
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 8e2cea7a8c78..6d8a48799112 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -244,9 +244,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
{"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, KeyEvent.KEYCODE_L,
META_ON},
- {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
- KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyEvent.KEYCODE_N,
- META_ON | CTRL_ON},
{"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 32a3b7f2c9cc..22c86eb3a9b8 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -272,6 +272,19 @@ public class PhoneWindowManagerTests {
}
@Test
+ public void powerPress_withoutDreamManagerInternal_doesNotCrash() {
+ when(mDisplayPolicy.isAwake()).thenReturn(true);
+ mDreamManagerInternal = null;
+ initPhoneWindowManager();
+
+ // Power button pressed.
+ int eventTime = 0;
+ mPhoneWindowManager.powerPress(eventTime, 1, 0);
+
+ // verify no crash
+ }
+
+ @Test
public void powerPress_hubOrDreamOrSleep_hubAvailableLocks() {
when(mDisplayPolicy.isAwake()).thenReturn(true);
mContext.getTestablePermissions().setPermission(android.Manifest.permission.DEVICE_POWER,
@@ -352,5 +365,10 @@ public class PhoneWindowManagerTests {
WindowWakeUpPolicy getWindowWakeUpPolicy() {
return mock(WindowWakeUpPolicy.class);
}
+
+ @Override
+ DreamManagerInternal getDreamManagerInternal() {
+ return mDreamManagerInternal;
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 9e7575f1c644..f5bda9f2b9e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -104,7 +104,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
.setTask(mTrampolineActivity.getTask())
.setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
.build();
- mTopActivity.mDisplayContent.mOpeningApps.add(mTopActivity);
mTransition = new Transition(TRANSIT_OPEN, 0 /* flags */,
mTopActivity.mTransitionController, createTestBLASTSyncEngine());
mTransition.mParticipants.add(mTopActivity);
@@ -485,7 +484,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testActivityDrawnWithoutTransition() {
- mTopActivity.mDisplayContent.mOpeningApps.remove(mTopActivity);
mTransition.mParticipants.remove(mTopActivity);
onIntentStarted(mTopActivity.intent);
notifyAndVerifyActivityLaunched(mTopActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index bb296148dad1..d53ba1d24d8c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -992,6 +992,27 @@ public class ActivityRecordTests extends WindowTestsBase {
assertEquals(persistentSavedState, activity.getPersistentSavedState());
}
+ @Test
+ public void testReadWindowStyle() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setActivityTheme(
+ com.android.frameworks.wmtests.R.style.ActivityWindowStyleTest).build();
+ assertTrue(activity.isNoDisplay());
+ assertTrue("Fill parent because showWallpaper", activity.mStyleFillsParent);
+
+ final ActivityRecord.WindowStyle style = mAtm.getWindowStyle(
+ activity.packageName, activity.info.theme, activity.mUserId);
+ assertNotNull(style);
+ assertTrue(style.isTranslucent());
+ assertTrue(style.isFloating());
+ assertTrue(style.showWallpaper());
+ assertTrue(style.noDisplay());
+ assertTrue(style.disablePreview());
+ assertTrue(style.optOutEdgeToEdge());
+ assertEquals(1 /* icon_preferred */, style.mSplashScreenBehavior);
+ assertEquals(style.noDisplay(), mAtm.mInternal.isNoDisplay(activity.packageName,
+ activity.info.theme, activity.mUserId));
+ }
+
/**
* Verify that activity finish request is not performed if activity is finishing or is in
* incorrect state.
@@ -3326,16 +3347,13 @@ public class ActivityRecordTests extends WindowTestsBase {
makeWindowVisibleAndDrawn(app);
// Put the activity in close transition.
- mDisplayContent.mOpeningApps.clear();
- mDisplayContent.mClosingApps.add(app.mActivityRecord);
- mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+ requestTransition(app.mActivityRecord, WindowManager.TRANSIT_CLOSE);
// Remove window during transition, so it is requested to hide, but won't be committed until
// the transition is finished.
app.mActivityRecord.onRemovedFromDisplay();
app.mActivityRecord.prepareSurfaces();
- assertTrue(mDisplayContent.mClosingApps.contains(app.mActivityRecord));
assertFalse(app.mActivityRecord.isVisibleRequested());
assertTrue(app.mActivityRecord.isVisible());
assertTrue(app.mActivityRecord.isSurfaceShowing());
@@ -3353,11 +3371,6 @@ public class ActivityRecordTests extends WindowTestsBase {
makeWindowVisibleAndDrawn(app);
app.mActivityRecord.prepareSurfaces();
- // Put the activity in close transition.
- mDisplayContent.mOpeningApps.clear();
- mDisplayContent.mClosingApps.add(app.mActivityRecord);
- mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
-
// Commit visibility before start transition.
app.mActivityRecord.commitVisibility(false, false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 3c74ad06a21f..a9be47d71213 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -509,6 +509,32 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(rootTask)).isTrue();
}
+ @Test
+ public void testOpaque_nonLeafTaskFragmentWithDirectActivity_opaque() {
+ final ActivityRecord directChildActivity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .build();
+ directChildActivity.setOccludesParent(true);
+ final Task nonLeafTask = directChildActivity.getTask();
+ final TaskFragment directChildFragment = new TaskFragment(mAtm, new Binder(),
+ true /* createdByOrganizer */, false /* isEmbedded */);
+ nonLeafTask.addChild(directChildFragment, 0);
+
+ assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(nonLeafTask)).isTrue();
+ }
+
+ @Test
+ public void testOpaque_nonLeafTaskFragmentWithDirectActivity_transparent() {
+ final ActivityRecord directChildActivity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .build();
+ directChildActivity.setOccludesParent(false);
+ final Task nonLeafTask = directChildActivity.getTask();
+ final TaskFragment directChildFragment = new TaskFragment(mAtm, new Binder(),
+ true /* createdByOrganizer */, false /* isEmbedded */);
+ nonLeafTask.addChild(directChildFragment, 0);
+
+ assertThat(mSupervisor.mOpaqueContainerHelper.isOpaque(nonLeafTask)).isFalse();
+ }
+
@NonNull
private TaskFragment createChildTaskFragment(@NonNull Task parent,
@WindowConfiguration.WindowingMode int windowingMode,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 018ea58e7120..d016e735f0c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -151,6 +151,10 @@ class AppCompatActivityRobot {
doReturn(taskBounds).when(mTaskStack.top()).getBounds();
}
+ void configureTaskAppBounds(@NonNull Rect appBounds) {
+ mTaskStack.top().getWindowConfiguration().setAppBounds(appBounds);
+ }
+
void configureTopActivityBounds(@NonNull Rect activityBounds) {
doReturn(activityBounds).when(mActivityStack.top()).getBounds();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
index 0c310324ce67..2603d787ae37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -27,6 +29,7 @@ import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -40,6 +43,7 @@ import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.window.flags.Flags;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -225,6 +229,53 @@ public class AppCompatLetterboxPolicyTest extends WindowTestsBase {
});
}
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ public void testGetRoundedCornersRadius_letterboxBoundsMatchHeightInFreeform_notRounded() {
+ runTestScenario((robot) -> {
+ robot.conf().setLetterboxActivityCornersRadius(15);
+ robot.configureWindowState();
+ robot.activity().createActivityWithComponent();
+ robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+ robot.activity().setTopActivityVisible(/* isVisible */ true);
+ robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+ robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+ robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+ robot.activity().setTaskWindowingMode(WINDOWING_MODE_FREEFORM);
+ final Rect appBounds = new Rect(0, 0, 100, 500);
+ robot.configureWindowStateFrame(appBounds);
+ robot.activity().configureTaskAppBounds(appBounds);
+
+ robot.startLetterbox();
+
+ robot.checkWindowStateRoundedCornersRadius(/* expected */ 0);
+ });
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ public void testGetRoundedCornersRadius_letterboxBoundsNotMatchHeightInFreeform_rounded() {
+ runTestScenario((robot) -> {
+ robot.conf().setLetterboxActivityCornersRadius(15);
+ robot.configureWindowState();
+ robot.activity().createActivityWithComponent();
+ robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+ robot.activity().setTopActivityVisible(/* isVisible */ true);
+ robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+ robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+ robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+ robot.activity().setTaskWindowingMode(WINDOWING_MODE_FREEFORM);
+ robot.configureWindowStateFrame(new Rect(0, 0, 500, 200));
+ robot.activity().configureTaskAppBounds(new Rect(0, 0, 500, 500));
+
+ robot.startLetterbox();
+
+ robot.checkWindowStateRoundedCornersRadius(/* expected */ 15);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -265,6 +316,10 @@ public class AppCompatLetterboxPolicyTest extends WindowTestsBase {
spyOn(getTransparentPolicy());
}
+ void startLetterbox() {
+ getAppCompatLetterboxPolicy().start(mWindowState);
+ }
+
void configureWindowStateWithTaskBar(boolean hasInsetsRoundedCorners) {
configureWindowState(/* withTaskBar */ true, hasInsetsRoundedCorners);
}
@@ -273,6 +328,10 @@ public class AppCompatLetterboxPolicyTest extends WindowTestsBase {
configureWindowState(/* withTaskBar */ false, /* hasInsetsRoundedCorners */ false);
}
+ void configureWindowStateFrame(@NonNull Rect frame) {
+ doReturn(frame).when(mWindowState).getFrame();
+ }
+
void configureInsetsRoundedCorners(@NonNull RoundedCorners roundedCorners) {
mInsetsState.setRoundedCorners(roundedCorners);
}
@@ -290,6 +349,7 @@ public class AppCompatLetterboxPolicyTest extends WindowTestsBase {
}
mWindowState.mInvGlobalScale = 1f;
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+ attrs.type = TYPE_BASE_APPLICATION;
doReturn(mInsetsState).when(mWindowState).getInsetsState();
doReturn(attrs).when(mWindowState).getAttrs();
doReturn(true).when(mWindowState).isDrawn();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index fbb123e25b29..e08197155f03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -294,6 +294,14 @@ public class BackNavigationControllerTests extends WindowTestsBase {
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
+
+ // reset drawing status, test previous activity has no process.
+ backNavigationInfo.onBackNavigationFinished(false);
+ mBackNavigationController.clearBackAnimations(true);
+ doReturn(false).when(testCase.recordBack).hasProcess();
+ backNavigationInfo = startBackNavigation();
+ assertThat(typeToString(backNavigationInfo.getType()))
+ .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
index eaffc481098e..1e91bedb5c18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
@@ -30,7 +28,6 @@ import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
import android.window.DesktopModeFlags;
import androidx.test.filters.SmallTest;
@@ -74,14 +71,12 @@ public class DesktopModeHelperTest {
doReturn(mContext.getContentResolver()).when(mMockContext).getContentResolver();
resetDesktopModeFlagsCache();
resetEnforceDeviceRestriction();
- resetFlagOverride();
}
@After
public void tearDown() throws Exception {
resetDesktopModeFlagsCache();
resetEnforceDeviceRestriction();
- resetFlagOverride();
}
@DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
@@ -167,7 +162,8 @@ public class DesktopModeHelperTest {
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@Test
- public void canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+ public void canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue()
+ throws Exception {
doReturn(true).when(mMockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
);
@@ -177,22 +173,41 @@ public class DesktopModeHelperTest {
}
@Test
- public void isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() {
+ public void isDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktop_returnsTrue() {
+ doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+ doReturn(true).when(mMockResources)
+ .getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops));
+
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
+ }
+
+ @Test
+ public void isDeviceEligibleForDesktopMode_configDEModeOffAndIntDispHostsDesktop_returnsFalse() {
+ doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+ doReturn(false).when(mMockResources)
+ .getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops));
+
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
+ }
+
+ @Test
+ public void isDeviceEligibleForDesktopMode_configDEModeOnAndIntDispHostsDesktopOff_returnsFalse() {
+ doReturn(false).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_canInternalDisplayHostDesktops));
- assertThat(DesktopModeHelper.isInternalDisplayEligibleToHostDesktops(mMockContext)).isTrue();
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
}
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@Test
public void isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() {
- assertThat(DesktopModeHelper.isInternalDisplayEligibleToHostDesktops(mMockContext)).isFalse();
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@Test
public void isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() {
- assertThat(DesktopModeHelper.isInternalDisplayEligibleToHostDesktops(mMockContext)).isFalse();
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@@ -202,7 +217,7 @@ public class DesktopModeHelperTest {
eq(R.bool.config_isDesktopModeDevOptionSupported)
);
- assertThat(DesktopModeHelper.isInternalDisplayEligibleToHostDesktops(mMockContext)).isTrue();
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
}
private void resetEnforceDeviceRestriction() throws Exception {
@@ -227,13 +242,10 @@ public class DesktopModeHelperTest {
cachedToggleOverride.set(/* obj= */ null, /* value= */ null);
}
- private void resetFlagOverride() {
- Settings.Global.putString(mContext.getContentResolver(),
- DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null);
- }
-
- private void setFlagOverride(DesktopModeFlags.ToggleOverride override) {
- Settings.Global.putInt(mContext.getContentResolver(),
- DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.getSetting());
+ private void setFlagOverride(DesktopModeFlags.ToggleOverride override) throws Exception {
+ Field cachedToggleOverride = DesktopModeFlags.class.getDeclaredField(
+ "sCachedToggleOverride");
+ cachedToggleOverride.setAccessible(true);
+ cachedToggleOverride.set(/* obj= */ null, /* value= */ override);
}
} \ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 82435b24dad6..d5b9751b0f51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -164,6 +164,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.BooleanSupplier;
/**
* Tests for the {@link DisplayContent} class.
@@ -1799,8 +1800,7 @@ public class DisplayContentTests extends WindowTestsBase {
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
app.setVisible(false);
app.setState(ActivityRecord.State.RESUMED, "test");
- mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
- mDisplayContent.mOpeningApps.add(app);
+ requestTransition(app, WindowManager.TRANSIT_OPEN);
final int newOrientation = getRotatedOrientation(mDisplayContent);
app.setRequestedOrientation(newOrientation);
@@ -2674,16 +2674,67 @@ public class DisplayContentTests extends WindowTestsBase {
public void testKeyguardGoingAwayWhileAodShown() {
mDisplayContent.getDisplayPolicy().setAwake(true);
- final WindowState appWin = newWindowBuilder("appWin", TYPE_APPLICATION).setDisplay(
- mDisplayContent).build();
- final ActivityRecord activity = appWin.mActivityRecord;
+ final KeyguardController keyguard = mAtm.mKeyguardController;
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final int displayId = mDisplayContent.getDisplayId();
+
+ final BooleanSupplier keyguardShowing = () -> keyguard.isKeyguardShowing(displayId);
+ final BooleanSupplier keyguardGoingAway = () -> keyguard.isKeyguardGoingAway(displayId);
+ final BooleanSupplier appVisible = activity::isVisibleRequested;
+
+ // Begin locked and in AOD
+ keyguard.setKeyguardShown(displayId, true /* keyguard */, true /* aod */);
+ assertFalse(keyguardGoingAway.getAsBoolean());
+ assertFalse(appVisible.getAsBoolean());
+
+ // Start unlocking from AOD.
+ keyguard.keyguardGoingAway(displayId, 0x0 /* flags */);
+ assertTrue(keyguardGoingAway.getAsBoolean());
+ assertTrue(appVisible.getAsBoolean());
- mAtm.mKeyguardController.setKeyguardShown(appWin.getDisplayId(), true /* keyguardShowing */,
- true /* aodShowing */);
- assertFalse(activity.isVisibleRequested());
+ // Clear AOD. This does *not* clear the going-away status.
+ keyguard.setKeyguardShown(displayId, true /* keyguard */, false /* aod */);
+ assertTrue(keyguardGoingAway.getAsBoolean());
+ assertTrue(appVisible.getAsBoolean());
+
+ // Finish unlock
+ keyguard.setKeyguardShown(displayId, false /* keyguard */, false /* aod */);
+ assertFalse(keyguardGoingAway.getAsBoolean());
+ assertTrue(appVisible.getAsBoolean());
+ }
+
+ @Test
+ public void testKeyguardGoingAwayCanceledWhileAodShown() {
+ mDisplayContent.getDisplayPolicy().setAwake(true);
- mAtm.mKeyguardController.keyguardGoingAway(appWin.getDisplayId(), 0 /* flags */);
- assertTrue(activity.isVisibleRequested());
+ final KeyguardController keyguard = mAtm.mKeyguardController;
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final int displayId = mDisplayContent.getDisplayId();
+
+ final BooleanSupplier keyguardShowing = () -> keyguard.isKeyguardShowing(displayId);
+ final BooleanSupplier keyguardGoingAway = () -> keyguard.isKeyguardGoingAway(displayId);
+ final BooleanSupplier appVisible = activity::isVisibleRequested;
+
+ // Begin locked and in AOD
+ keyguard.setKeyguardShown(displayId, true /* keyguard */, true /* aod */);
+ assertFalse(keyguardGoingAway.getAsBoolean());
+ assertFalse(appVisible.getAsBoolean());
+
+ // Start unlocking from AOD.
+ keyguard.keyguardGoingAway(displayId, 0x0 /* flags */);
+ assertTrue(keyguardGoingAway.getAsBoolean());
+ assertTrue(appVisible.getAsBoolean());
+
+ // Clear AOD. This does *not* clear the going-away status.
+ keyguard.setKeyguardShown(displayId, true /* keyguard */, false /* aod */);
+ assertTrue(keyguardGoingAway.getAsBoolean());
+ assertTrue(appVisible.getAsBoolean());
+
+ // Same API call a second time cancels the unlock, because AOD isn't changing.
+ keyguard.setKeyguardShown(displayId, true /* keyguard */, false /* aod */);
+ assertTrue(keyguardShowing.getAsBoolean());
+ assertFalse(keyguardGoingAway.getAsBoolean());
+ assertFalse(appVisible.getAsBoolean());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
index db90c28ec7df..2d4101e40615 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
@@ -17,17 +17,21 @@
package com.android.server.wm;
import static android.view.Display.FLAG_PRESENTATION;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.window.flags.Flags.FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.UserHandle;
-import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
@@ -41,6 +45,7 @@ import android.view.WindowManagerGlobal;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,35 +58,100 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
public class PresentationControllerTests extends WindowTestsBase {
+ TestTransitionPlayer mPlayer;
+
+ @Before
+ public void setUp() {
+ mPlayer = registerTestTransitionPlayer();
+ }
+
@EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
@Test
- public void testPresentationHidesActivitiesBehind() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.copyFrom(mDisplayInfo);
- displayInfo.flags = FLAG_PRESENTATION;
- final DisplayContent dc = createNewDisplay(displayInfo);
- final int displayId = dc.getDisplayId();
- doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ public void testPresentationShowAndHide() {
+ final DisplayContent dc = createPresentationDisplay();
final ActivityRecord activity = createActivityRecord(createTask(dc));
assertTrue(activity.isVisible());
- doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled());
- final int uid = 100000; // uid for non-system user
+ // Add a presentation window, which requests the activity to stop.
+ final WindowState window = addPresentationWindow(100000, dc.mDisplayId);
+ assertFalse(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+ final Transition addTransition = window.mTransitionController.getCollectingTransition();
+ assertEquals(TRANSIT_OPEN, addTransition.mType);
+ assertTrue(addTransition.isInTransition(window));
+ assertTrue(addTransition.isInTransition(activity));
+
+ // Completing the transition makes the activity invisible.
+ completeTransition(addTransition, /*abortSync=*/ true);
+ assertFalse(activity.isVisible());
+
+ // Remove a Presentation window, which requests the activity to be resumed back.
+ window.removeIfPossible();
+ final Transition removeTransition = window.mTransitionController.getCollectingTransition();
+ assertEquals(TRANSIT_CLOSE, removeTransition.mType);
+ assertTrue(removeTransition.isInTransition(window));
+ assertTrue(removeTransition.isInTransition(activity));
+ assertTrue(activity.isVisibleRequested());
+ assertFalse(activity.isVisible());
+
+ // Completing the transition makes the activity visible.
+ completeTransition(removeTransition, /*abortSync=*/ false);
+ assertTrue(activity.isVisible());
+ }
+
+ @DisableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
+ @Test
+ public void testPresentationShowAndHide_flagDisabled() {
+ final DisplayContent dc = createPresentationDisplay();
+ final ActivityRecord activity = createActivityRecord(createTask(dc));
+ assertTrue(activity.isVisible());
+
+ final WindowState window = addPresentationWindow(100000, dc.mDisplayId);
+ assertFalse(window.mTransitionController.isCollecting());
+ assertTrue(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+
+ window.removeIfPossible();
+ assertFalse(window.mTransitionController.isCollecting());
+ assertTrue(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+ assertFalse(window.isAttached());
+ }
+
+ private WindowState addPresentationWindow(int uid, int displayId) {
final Session session = createTestSession(mAtm, 1234 /* pid */, uid);
final int userId = UserHandle.getUserId(uid);
- doReturn(false).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId));
+ doReturn(true).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId));
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_PRESENTATION);
-
final IWindow clientWindow = new TestIWindow();
- final int result = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId,
+ final int res = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId,
userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
new InsetsSourceControl.Array(), new Rect(), new float[1]);
- assertTrue(result >= WindowManagerGlobal.ADD_OKAY);
- assertFalse(activity.isVisible());
-
+ assertTrue(res >= WindowManagerGlobal.ADD_OKAY);
final WindowState window = mWm.windowForClientLocked(session, clientWindow, false);
- window.removeImmediately();
- assertTrue(activity.isVisible());
+ window.mHasSurface = true;
+ return window;
+ }
+
+ private DisplayContent createPresentationDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.copyFrom(mDisplayInfo);
+ displayInfo.flags = FLAG_PRESENTATION;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+ final int displayId = dc.getDisplayId();
+ doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ return dc;
+ }
+
+ private void completeTransition(@NonNull Transition transition, boolean abortSync) {
+ final ActionChain chain = ActionChain.testFinish(transition);
+ if (abortSync) {
+ // Forcefully finishing the active sync for testing purpose.
+ mWm.mSyncEngine.abort(transition.getSyncId());
+ } else {
+ transition.onTransactionReady(transition.getSyncId(), mTransaction);
+ }
+ transition.finishTransition(chain);
}
}
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 95bca2b17efb..1dc32b00acba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4486,6 +4486,49 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_EXCLUDE_CAPTION_FROM_APP_BOUNDS)
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
+ public void testInFreeform_boundsSandboxedToAppBounds() {
+ allowDesktopMode();
+ final int dw = 2800;
+ final int dh = 1400;
+ final int notchHeight = 100;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh)
+ .setNotch(notchHeight)
+ .build();
+ setUpApp(display);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ Rect appBounds = new Rect(0, 0, 1000, 500);
+ Rect bounds = new Rect(0, 0, 1000, 600);
+ mTask.getWindowConfiguration().setAppBounds(appBounds);
+ mTask.getWindowConfiguration().setBounds(bounds);
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+ // Bounds are sandboxed to appBounds in freeform.
+ assertDownScaled();
+ assertEquals(mActivity.getWindowConfiguration().getAppBounds(),
+ mActivity.getWindowConfiguration().getBounds());
+
+ // Exit freeform.
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mTask.getWindowConfiguration().setBounds(new Rect(0, 0, dw, dh));
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ assertFitted();
+ appBounds = mActivity.getWindowConfiguration().getAppBounds();
+ bounds = mActivity.getWindowConfiguration().getBounds();
+ // Bounds are not sandboxed to appBounds.
+ assertNotEquals(appBounds, bounds);
+ assertEquals(notchHeight, appBounds.top - bounds.top);
+ }
+
+
+ @Test
@EnableFlags(Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES)
public void testUserAspectRatioOverridesNotAppliedToResizeableFreeformActivity() {
final TaskBuilder taskBuilder =
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 59335d3bb135..9406779c929d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -98,6 +98,7 @@ import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.testutils.StubTransaction;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.window.flags.Flags;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -657,6 +658,13 @@ public class SystemServicesTestRule implements TestRule {
AppWarnings appWarnings = getAppWarningsLocked();
spyOn(appWarnings);
doNothing().when(appWarnings).onStartActivity(any());
+
+ if (Flags.trackSystemUiContextBeforeWms()) {
+ final Context uiContext = getUiContext();
+ spyOn(uiContext);
+ doNothing().when(uiContext).registerComponentCallbacks(any());
+ doNothing().when(uiContext).unregisterComponentCallbacks(any());
+ }
}
@Override
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 7ab55bf7e874..cc2a76dcc9f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -189,11 +189,12 @@ public class TaskFragmentTest extends WindowTestsBase {
doReturn(true).when(mTaskFragment).isVisible();
doReturn(true).when(mTaskFragment).isVisibleRequested();
+ spyOn(mTaskFragment.mTransitionController);
clearInvocations(mTransaction);
mTaskFragment.setBounds(endBounds);
// No change transition, but update the organized surface position.
- verify(mTaskFragment, never()).initializeChangeTransition(any(), any());
+ verify(mTaskFragment.mTransitionController, never()).collectVisibleChange(any());
verify(mTransaction).setPosition(mLeash, endBounds.left, endBounds.top);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ccce57a81e41..b1525cf00dc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -30,6 +30,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -39,6 +40,7 @@ import android.view.DisplayCutout;
import android.view.DisplayInfo;
import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
+import com.android.window.flags.Flags;
class TestDisplayContent extends DisplayContent {
@@ -79,6 +81,13 @@ class TestDisplayContent extends DisplayContent {
WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
+ if (Flags.trackSystemUiContextBeforeWms()) {
+ final Context uiContext = getDisplayUiContext();
+ spyOn(uiContext);
+ doNothing().when(uiContext).registerComponentCallbacks(any());
+ doNothing().when(uiContext).unregisterComponentCallbacks(any());
+ }
+
// For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
// See DisplayRotation#readDefaultDisplayRotation for context.
// Without that, meaning of height and width in context of the tests can be swapped if
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2ee34d3a4b36..77ad7f7bac7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -683,7 +683,7 @@ public class TransitionTests extends WindowTestsBase {
app.onStartingWindowDrawn();
// The task appeared event should be deferred until transition ready.
assertFalse(task.taskAppearedReady());
- testPlayer.onTransactionReady(app.getSyncTransaction());
+ testPlayer.onTransactionReady();
assertTrue(task.taskAppearedReady());
assertTrue(playerProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
@@ -1162,7 +1162,8 @@ public class TransitionTests extends WindowTestsBase {
screenDecor.updateSurfacePosition(mMockT);
assertEquals(prevPos, screenDecor.mLastSurfacePosition);
- final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction startTransaction = mTransaction;
+ clearInvocations(startTransaction);
final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
onRotationTransactionReady(player, startTransaction);
@@ -1213,7 +1214,8 @@ public class TransitionTests extends WindowTestsBase {
assertFalse(statusBar.mToken.inTransition());
assertTrue(app.getTask().inTransition());
- final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction startTransaction = mTransaction;
+ clearInvocations(startTransaction);
final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
doReturn(true).when(leash).isValid();
final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
@@ -1287,7 +1289,8 @@ public class TransitionTests extends WindowTestsBase {
// Avoid DeviceStateController disturbing the test by triggering another rotation change.
doReturn(false).when(mDisplayContent).updateRotationUnchecked();
- onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
+ clearInvocations(mTransaction);
+ onRotationTransactionReady(player, mTransaction).onTransactionCommitted();
assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
spyOn(navBarInsetsProvider);
@@ -1350,7 +1353,7 @@ public class TransitionTests extends WindowTestsBase {
mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
doReturn(true).when(home).hasFixedRotationTransform(any());
player.startTransition();
- player.onTransactionReady(mDisplayContent.getSyncTransaction());
+ player.onTransactionReady();
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
final RemoteDisplayChangeController displayChangeController = mDisplayContent
@@ -3071,8 +3074,11 @@ public class TransitionTests extends WindowTestsBase {
TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
- player.onTransactionReady(startTransaction);
- verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
+ player.onTransactionReady();
+ // The startTransaction is from mWm.mTransactionFactory.get() in SyncGroup#finishNow.
+ // 2 times are from SyncGroup#finishNow and AsyncRotationController#setupStartTransaction.
+ verify(startTransaction, times(2)).addTransactionCommittedListener(
+ any(), listenerCaptor.capture());
return listenerCaptor.getValue();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index edffab801499..cee98fb1b34c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -409,17 +409,6 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Test
- public void testIsAnimating_TransitionFlag() {
- final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
- final TestWindowContainer root = builder.setLayer(0).build();
- final TestWindowContainer child1 = root.addChildWindow(
- builder.setWaitForTransitionStart(true));
-
- assertFalse(root.isAnimating(TRANSITION));
- assertTrue(child1.isAnimating(TRANSITION));
- }
-
- @Test
public void testIsAnimating_ParentsFlag() {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
@@ -1655,7 +1644,7 @@ public class WindowContainerTests extends WindowTestsBase {
};
TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating,
- boolean isVisible, boolean waitTransitStart, Integer orientation, WindowState ws) {
+ boolean isVisible, Integer orientation, WindowState ws) {
super(wm);
mLayer = layer;
@@ -1663,7 +1652,6 @@ public class WindowContainerTests extends WindowTestsBase {
mIsVisible = isVisible;
mFillsParent = true;
mOrientation = orientation;
- mWaitForTransitStart = waitTransitStart;
mWindowState = ws;
spyOn(mSurfaceAnimator);
doReturn(mIsAnimating).when(mSurfaceAnimator).isAnimating();
@@ -1729,11 +1717,6 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Override
- boolean isWaitingForTransitionStart() {
- return mWaitForTransitStart;
- }
-
- @Override
WindowState asWindowState() {
return mWindowState;
}
@@ -1744,7 +1727,6 @@ public class WindowContainerTests extends WindowTestsBase {
private int mLayer;
private boolean mIsAnimating;
private boolean mIsVisible;
- private boolean mIsWaitTransitStart;
private Integer mOrientation;
private WindowState mWindowState;
@@ -1782,14 +1764,9 @@ public class WindowContainerTests extends WindowTestsBase {
return this;
}
- TestWindowContainerBuilder setWaitForTransitionStart(boolean waitTransitStart) {
- mIsWaitTransitStart = waitTransitStart;
- return this;
- }
-
TestWindowContainer build() {
return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible,
- mIsWaitTransitStart, mOrientation, mWindowState);
+ mOrientation, mWindowState);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 1dfb20a41816..d228970e0371 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -33,9 +34,11 @@ import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ConfigurationContainer.applySizeOverrideIfNeeded;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -58,6 +61,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.LocaleList;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
@@ -453,6 +457,56 @@ public class WindowProcessControllerTests extends WindowTestsBase {
assertEquals(topDisplayArea, mWpc.getTopActivityDisplayArea());
}
+ @Test
+ @EnableFlags(com.android.window.flags.Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION)
+ public void testOverrideConfigurationApplied() {
+ final DisplayContent displayContent = new TestDisplayContent.Builder(mAtm, 1000, 1500)
+ .setSystemDecorations(true).setDensityDpi(160).build();
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+ // Setup the decor insets info.
+ final DisplayPolicy.DecorInsets.Info decorInsetsInfo = new DisplayPolicy.DecorInsets.Info();
+ final Rect emptyRect = new Rect();
+ decorInsetsInfo.mNonDecorInsets.set(emptyRect);
+ decorInsetsInfo.mConfigInsets.set(emptyRect);
+ decorInsetsInfo.mOverrideConfigInsets.set(new Rect(0, 100, 0, 200));
+ decorInsetsInfo.mOverrideNonDecorInsets.set(new Rect(0, 0, 0, 200));
+ decorInsetsInfo.mNonDecorFrame.set(new Rect(0, 0, 1000, 1500));
+ decorInsetsInfo.mConfigFrame.set(new Rect(0, 0, 1000, 1500));
+ decorInsetsInfo.mOverrideConfigFrame.set(new Rect(0, 100, 1000, 1300));
+ decorInsetsInfo.mOverrideNonDecorFrame.set(new Rect(0, 0, 1000, 1300));
+ doReturn(decorInsetsInfo).when(displayPolicy)
+ .getDecorInsetsInfo(anyInt(), anyInt(), anyInt());
+
+ final Configuration newParentConfig = displayContent.getConfiguration();
+ final Configuration resolvedConfig = new Configuration();
+
+ // Mock the app info to not enforce the decoupled configuration to apply the override.
+ final ApplicationInfo appInfo = mock(ApplicationInfo.class);
+ doReturn(false).when(appInfo)
+ .isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED);
+ doReturn(false).when(appInfo)
+ .isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
+
+ // No value should be set before override.
+ assertNull(resolvedConfig.windowConfiguration.getAppBounds());
+ applySizeOverrideIfNeeded(
+ displayContent,
+ appInfo,
+ newParentConfig,
+ resolvedConfig,
+ false /* optsOutEdgeToEdge */,
+ false /* hasFixedRotationTransform */,
+ false /* hasCompatDisplayInsets */,
+ null /* task */);
+
+ // Assert the override config insets are applied.
+ // Status bars, and all non-decor insets should be deducted for the config screen size.
+ assertEquals(1200, resolvedConfig.screenHeightDp);
+ // Only the non-decor insets should be deducted for the app bounds.
+ assertNotNull(resolvedConfig.windowConfiguration.getAppBounds());
+ assertEquals(1300, resolvedConfig.windowConfiguration.getAppBounds().height());
+ }
+
private TestDisplayContent createTestDisplayContentInContainer() {
return new TestDisplayContent.Builder(mAtm, 1000, 1500).build();
}
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 7f9e591ca5e3..c6416850c464 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -861,11 +861,9 @@ public class WindowTestsBase extends SystemServiceTestsBase {
/** Creates a {@link DisplayContent} and adds it to the system. */
private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy,
@Nullable SettingsEntry overrideSettings) {
- final DisplayContent display =
- new TestDisplayContent.Builder(mAtm, info)
- .setOverrideSettings(overrideSettings)
- .build();
- final DisplayContent dc = display.mDisplayContent;
+ final DisplayContent dc = new TestDisplayContent.Builder(mAtm, info)
+ .setOverrideSettings(overrideSettings)
+ .build();
// this display can show IME.
dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy);
return dc;
@@ -2161,13 +2159,14 @@ public class WindowTestsBase extends SystemServiceTestsBase {
mOrganizer.startTransition(mLastTransit.getToken(), null);
}
- void onTransactionReady(SurfaceControl.Transaction t) {
- mLastTransit.onTransactionReady(mLastTransit.getSyncId(), t);
+ void onTransactionReady() {
+ // SyncGroup#finishNow -> Transition#onTransactionReady.
+ mController.mSyncEngine.abort(mLastTransit.getSyncId());
}
void start() {
startTransition();
- onTransactionReady(mock(SurfaceControl.Transaction.class));
+ onTransactionReady();
}
public void finish() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
index 12b744546f5e..9367941e32a3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -18,12 +18,16 @@ package com.android.server.wm;
import static android.tools.traces.Utils.busyWaitForDataSourceRegistration;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -34,11 +38,15 @@ import android.platform.test.annotations.Presubmit;
import android.tools.ScenarioBuilder;
import android.tools.traces.io.ResultWriter;
import android.tools.traces.monitors.PerfettoTraceMonitor;
+import android.util.Log;
import android.view.Choreographer;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import androidx.test.uiautomator.UiDevice;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -51,14 +59,15 @@ import java.io.IOException;
/**
* Test class for {@link WindowTracingPerfetto}.
*/
+@FlakyTest(bugId = 372558379)
@SmallTest
@Presubmit
public class WindowTracingPerfettoTest {
private static final String TEST_DATA_SOURCE_NAME = "android.windowmanager.test";
private static WindowManagerService sWmMock;
- private static Choreographer sChoreographer;
private static WindowTracing sWindowTracing;
+ private static Boolean sIsDataSourceRegisteredSuccessfully;
private PerfettoTraceMonitor mTraceMonitor;
@@ -66,19 +75,39 @@ public class WindowTracingPerfettoTest {
public static void setUpOnce() throws Exception {
sWmMock = Mockito.mock(WindowManagerService.class);
Mockito.doNothing().when(sWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt());
- sChoreographer = Mockito.mock(Choreographer.class);
- sWindowTracing = new WindowTracingPerfetto(sWmMock, sChoreographer,
+ sWindowTracing = new WindowTracingPerfetto(sWmMock, Mockito.mock(Choreographer.class),
new WindowManagerGlobalLock(), TEST_DATA_SOURCE_NAME);
- busyWaitForDataSourceRegistration(TEST_DATA_SOURCE_NAME);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ sWmMock = null;
+ sWindowTracing = null;
}
@Before
public void setUp() throws IOException {
- Mockito.clearInvocations(sWmMock);
+ if (sIsDataSourceRegisteredSuccessfully != null) {
+ assumeTrue("Failed to register data source", sIsDataSourceRegisteredSuccessfully);
+ return;
+ }
+ try {
+ busyWaitForDataSourceRegistration(TEST_DATA_SOURCE_NAME);
+ sIsDataSourceRegisteredSuccessfully = true;
+ } catch (Exception e) {
+ sIsDataSourceRegisteredSuccessfully = false;
+ final String perfettoStatus = UiDevice.getInstance(getInstrumentation())
+ .executeShellCommand("perfetto --query");
+ Log.e(WindowTracingPerfettoTest.class.getSimpleName(),
+ "Failed to register data source: " + perfettoStatus);
+ // Only fail once. The rest tests will be skipped by assumeTrue.
+ fail("Failed to register data source");
+ }
}
@After
public void tearDown() throws IOException {
+ Mockito.clearInvocations(sWmMock);
stopTracing();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WindowStyleCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WindowStyleCacheTest.java
new file mode 100644
index 000000000000..57a340129456
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WindowStyleCacheTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.policy.AttributeCache;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ * atest WmTests:WindowStyleCacheTest
+ */
+@SmallTest
+@Presubmit
+public class WindowStyleCacheTest {
+
+ @Test
+ public void testCache() {
+ final Context context = getInstrumentation().getContext();
+ AttributeCache.init(context);
+ final WindowStyleCache<TestStyle> cache = new WindowStyleCache<>(TestStyle::new);
+ final String packageName = context.getPackageName();
+ final int theme = com.android.frameworks.wmtests.R.style.ActivityWindowStyleTest;
+ final int userId = context.getUserId();
+ final TestStyle style = cache.get(packageName, theme, userId);
+ assertNotNull(style);
+ assertSame(style, cache.get(packageName, theme, userId));
+
+ cache.invalidatePackage(packageName);
+ assertNotSame(style, cache.get(packageName, theme, userId));
+ }
+
+ private static class TestStyle {
+ TestStyle(TypedArray array) {
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index ec4f7e1ea4ba..4395b76d91cc 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -52,6 +52,7 @@ import android.os.Bundle;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
@@ -694,6 +695,11 @@ public class UsbService extends IUsbManager.Stub {
return (getCurrentFunctions() & UsbManager.usbFunctionsFromString(function)) != 0;
}
+ @Override
+ public boolean isUvcGadgetSupportEnabled() {
+ return SystemProperties.getBoolean("ro.usb.uvc.enabled", false);
+ }
+
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USB)
@Override
public long getCurrentFunctions() {